前回 は、IkaLogの画像処理の基本的な考え方、また、その背景などについて紹介しました。今回は、IkaLogの公開に向けてどのような取り組みを行ったかについて紹介します。
Windows環境では「コンパイル済みのOpenCV、NumPy配布物」を活用
当初から今まで、IkaLogの開発はMacOS X環境で行っています。しかし、Wii Uの画面をキャプチャできるHDMIキャプチャデバイスの多くはWindows向けの製品です。私自身も、当初はWindows向けのHDMIキャプチャデバイスしか持っておらず、Windows環境で録画したスプラトゥーンのビデオファイルやスクリーンショットを使って開発していました。このため、まずは「書いたコードをWindowsで動かす」という課題に取り組むことにしました。
OpenCVのコンパイルは、とくにWindows上では大変な作業になります。PythonからOpenCVを使いたいだけなのに、OpenCVをコンパイルする過程でVisual Studioの特定バージョンも必要になります。IkaLogを使うためにそこまで準備の手間はかけられません。
私自身もOpenCVのコンパイルに時間を費やしたくはないのに、IkaLogのユーザーとして想定される方(ゲーム好きな人々)がPythonやOpenCVを自前でコンパイルできることは想定しづらいです。このため、Windows上でIkaLogの実行できる環境は、Python本家のバイナリ配布物、および有志の方が公開しているWindows向けOpenCVバイナリなどを利用して構築できるようにしようと考えました。
調べていたところ、下記サイトでさまざまなPython向けのライブラリをコンパイルしたものが用意されているので、こちらから必要なコンパイル済みファイルを取得してもらうことにしました。私が使っているWindows版IkaLogの開発/ビルド環境でも、このサイトで提供されているモジュールを用いています。
こちらのサイトで配布されているNumPyは、大量の演算処理を高速に行えるIntel Math Kernel Library(MKL)とリンクされています。このIntel MKLがNumPyを高速化しており、IkaLogの認識アルゴリズムが原始的で計算量が最適化されていなくても、今時のコンピュータではさくさくっと動いてしまう理由だったりします。
映像のキャプチャはどうやって実現する?
先の配布サイトなどからPython、OpenCV、そしてNumPyをそろえてIkaLogのコードを実行したところ、それまでに開発したコードはあっけないほどかんたんに、そのまま期待どおり動作しました。開発当初から使用していたビデオファイルも無事に処理でき、IkaLogがWindows環境でも利用できることが確認できました。
しかし、この時点ではHDMIキャプチャデバイスとの連携については考慮しておらず、使えるのは事前に録画されたビデオファイルのみでした。IkaLog開発をはじめてから数週間は「画像認識」というコンセプトで機能の実装に取り込んできたわけですが、それらがひと段落したため、実際のユースケースとして「Wii Uからのビデオ信号をリアルタイムに処理する」ためにはどうしたらいいか、検討を始めました。
当初の作戦は、FFmpegなど、DirectShowの入力デバイスからビデオストリームを拾えるソフトウェアからパイプなどの仕組みで映像データをもらうことを想定していました。こうすれば、面倒そうなビデオキャプチャデバイスのハンドリングを、実績がある既存のソフトウェアに任せられるだろうと考えたからです。「 この作戦がダメな場合には、PythonからDirectShowなどを直接制御する必要があるかもしれない」ということは考えていました。
OpenCVでビデオキャプチャに成功
そんなことをぼんやり考えながら、OpenCVにもビデオキャプチャを行うVideoCapture機能があることを思い出し、これが利用できるか検討をはじめました。OpenCVは映像処理に使われるさまざまな機能を持ったライブラリですが、その中にWebカメラなどから映像をキャプチャ入力する機能としてVideoCaptureが含まれています。
「動いたらラッキー」ぐらいの気持ちで、VideoCaptureの機能を使ってHDMIキャプチャデバイスを操作してみたところ、手元HDMIキャプチャデバイスは以下のようなシンプルなコードで画像を間欠的に取得できることがわかりました。
#!/usr/bin python
# -*- coding: utf-8 -*-
import cv2
# キャプチャデバイスの番号(1つ目のデバイス=0、2つ目=1, ...)
source_device = 0
# OpenCVライブラリのVideoCaptureを初期化し、
# 入力フォーマットを1280x720に設定
cap = cv2.VideoCapture(source_device)
cap.set(3, 1280)
cap.set(4, 720)
# ESC キーが押下されるまで、入力されたキャプチャ映像を表示
while cv2.waitKey(1) != 27:
r, img = cap.read()
if not r:
continue
cv2.imshow('img', img)
図 OpenCVによるキャプチャの例
OpenCVのVideoCaptureは、コンピュータビジョンの研究開発において、USBカメラなどからの映像入力を目的として利用されることがほとんどでしょう。しかし、USBカメラもHDMIキャプチャデバイスも、OpenCVからすればカメラデバイスの実装の1つにすぎないので、究極的にはそのまま使えるわけです。
実際には、OpenCVのビデオキャプチャ機能がデバイスと相性問題を起こしやすいこともわかっており、この方法では動作しないHDMIキャプチャデバイスがいくつも存在します(この件については、今後の回で触れたいと思います) 。しかし、IkaLogの最初のバージョンとしては「少なくとも動く構成がある!!」というだけでも十分だと判断し、最初のバージョンではOpenCVのVideoCaptureを介した入力のみをサポートすることにしました。もし、手元のHDMIキャプチャデバイスとOpenCV VideoCaptureの相性がよくなければ、IkaLogは手元でコンセプト止まりになり、公開まで至っていなかったかもしれません。
この実装上の制約により、「 IkaLogで使用できるHDMIビデオキャプチャデバイスは少数に限られるだろう」と考えていました。しかし、IkaLog公開後、実際に試してくださったユーザーの方が、「 アマレコTV というソフトウェアと組み合わせられる」ことに気づき、その旨を教えていただきました。これは、「 特定のビデオキャプチャデバイスしか使えない」と思っていたIkaLogが「アマレコTVを併用すれば、ほとんどのキャプチャデバイスで利用できる」ということであり、私の当初想定より広い環境でIkaLogを動作しうる、ということを意味します。このアマレコTVという優れたソフトウェアが存在していなければ、IkaLogはここまで使ってもらえるようにはならなかったでしょう。
GitHubでIkaLogを公開!
このような経緯を経て、IkaLogがWii Uの映像をHDMIキャプチャデバイスで入力し、リアルタイムに追跡できるところまで実装でき、2015年8月16日、GitHub上にIkaLogを初公開しました 。なお、GitHubイニシャルカットの時点で搭載していた機能は下記のとおりです。
IkaLog イニシャルカットにおける認識項目
場面 認識項目
ゲーム開始時 ステージ名、ルール名を認識する
ゲーム終了時 ゲームの勝ち/負けを認識する
IkaLog イニシャルカットにおける対応アクション
アクション 説明
コンソール出力 勝敗情報をコンソールに出力
CSV出力 CSVファイル形式でゲームの勝敗記録をログ出力
JSON出力 JSONファイル形式でゲームの勝敗記録をログ出力
Twitter投稿 Twitterでゲーム結果をつぶやくbot
Slack投稿 Slackチャンネルにゲーム結果を投稿するbot
Fluentd出力 Fluentdにゲームの勝敗記録を転送
スクリーンショット保存 リザルト画面をスクリーンショットとして保存
ところで、最初のバージョンのIkaLogは、画像認識処理(ステージ名、ルール名、勝敗)が100行、各種アクションの実装が500行、合計で600行のPythonスクリプトでした。
一方、現在のIkaLogは、関連スクリプトを含めて23,000行を超えています。開発開始から約1年で、初回リリースと比べて38倍の規模にまで大きなソフトウェアになったようです。
しかし、反応は思わしくなく……
IkaLogを公開はしてみたものの、反応は思った以上に冷ややかでした。Twitterのタイムラインにスプラトゥーンのプレイ記録がどんどん流れるような夢(妄想?)を見ていたのですが、実際にはそうはなりませんでした。実際問題として、多くの方が無秩序にスプラトゥーンのプレイ記録を投稿すれば、Twitterのタイムラインがノイズだらけになり、それはそれで困惑するでしょう。リバースエンジニアリングなしで実装したいという私の思いとは裏腹に、「 どうしてプロトコルハックで済ませないのか?」といったコメントもいただきました。
IkaLogに対する反応が自分の想定を大幅に下回ったので、原因を自分なりに分析してみたところ、以下のような課題が思い浮かびました。
HDMIキャプチャデバイスを持っている人が少ないので、使える人が少ない
CSV/JSON、Fluetndで戦績を蓄積できても、それを分析できる人が少ない
IkaLogのためにPython3、OpenCVなどの環境を整備する元気のある人が少ない
IkaLogの設定ファイル(Pythonクラス)を書いてまで実行できる人が少ない
そもそも私以外に、こんなソフトウェアに期待している人がいなかった
ほかに理由はいろいろとあったかと思いますが、IkaLogでプレイヤーの興味が得られないのは、やはり「ソフトウェアとしての使い勝手の問題」だと考えました。「 HDMIキャプチャデバイスが必要である」という点は大きなハードルに思えますが、ニコニコ動画でゲームを投稿しているニコ生主はすでにHDMIビデオキャプチャカードを持っているのですから、スプラトゥーンのプレイヤーが買えない理由はありません。このため、1つの目標として「ニコ生主でもつかえるIkaLog」を目指して、Windows環境における利便性を上げる努力をすることにしました。
Windowsバイナリ版の開発秘話
GitHubに初回バージョンをリリースした後の改善として取り組んだのが、Windowsバイナリ版の開発です。
IkaLogを使いたいスプラトゥーンプレイヤーに、自分でPython3やOpenCVをはじめとする依存モジュールを導入できる方がどれだけいるでしょうか。多くの方は、そのような作業はできませんし、その時間があったらスプラトゥーンをプレイする時間に充てると思います(私でもそうします) 。
当初GitHubに公開したIkaLogは、「 とりあえず動作する」というレベルのPython 3で書かれたスクリプトでした(今でもそうですが……) 。しかし、スプラトゥーンのプレイヤー層へある程度普及させるためには、少なくともWindowsアプリケーションとして形を整えることが必要だろうと考えていました。そこで、下記の2つの要素に取り組み、Windowsバイナリ版のIkaLogを作ることにしました。
Py2EXEを用いて、Windows版バイナリ(.EXE形式の実行ファイル)を生成する
wxWidgetsを用いて、WindowsアプリケーションのようなGUIを提供する
PythonプログラムをEXE化する「Py2EXE」
PythonベースのプログラムをEXEファイルとして配布しているソフトウェアの例としてはDropboxのクライアントがよく知られていますが、Windowsバイナリ版のIkaLogもPythonコードを基に生成しています。
IkaLogのWindowsバイナリ版の生成には、Pythonスクリプトを.EXE形式に変更する仕組みとしてPy2EXE を使っています。Py2EXEは、かんたんに説明すると、下記の仕組みで動作します。
バイナリ生成時
Pythonプログラム(.py)を中間言語(.pyc)にコンパイルする
Pythonプログラム、依存しているモジュール群を1つのファイルにアーカイブする
上記と、Pythonインタプリタ、そしてローダを含めた実行形式(EXE)のファイルを生成する
実行時
実行形式(EXE)のファイルが、自身内のアーカイブから、Pythonスクリプトやモジュール(中間言語)を読み出す
内蔵のPythonのインタプリタを起動し、アーカイブ内のプログラムを実行する
Py2EXEされたIkaLogには実行に必要なモジュール群が含まれているため、OpenCVやNumPyなどのモジュールがセットアップされたPython実行環境を作らなくても、普通のWindowsプログラムのように実行できます。このため、Windows上でIkaLogを使う際に、Pythonのインストールなどに苦労する必要はなくなります。
具体的にPy2EXEを試してみましょう。たとえば、Hello Worldとだけメッセージを出力するhello_world.pyがあるとします。
print('hello world')
これをPy2EXEで実行形式にする場合、下記の内容でsetup.pyファイルを作成します。optionsには、IkaLogで使っているものに近いパラメータを与えてみました。
from distutils.core import setup
import py2exe
setup(
console=['hello_world.py'],
zipfile=None,
options={
'py2exe': {
'bundle_files': 1,
'unbuffered': True,
'optimize': 2,
'compressed': 1,
}
}
)
そして、Py2EXEを配布元からダウンロード、インストールした環境において、以下の要領で実行ファイルを作成します。
Z:\hasegaw\bitbucket\GihyoIkaLog\src\03>python setup.py py2exe
running py2exe
3 missing Modules
------------------
? readline imported from cmd, code, pdb
? win32api imported from platform
? win32con imported from platform
Building 'dist\hello_world.exe'.
Z:\hasegaw\bitbucket\GihyoIkaLog\src\03>dir dist
ドライブ Z のボリューム ラベルは Shared Folders です
ボリューム シリアル番号は 0000-0064 です
Z:\hasegaw\bitbucket\GihyoIkaLog\src\03\dist のディレクトリ
2016/06/15 21:37 <DIR> .
2016/06/15 21:37 <DIR> ..
2016/06/15 21:37 5,360,255 hello_world.exe
1 個のファイル 5,360,561 バイト
2 個のディレクトリ 142,070,349,824 バイトの空き領域
Z:\hasegaw\bitbucket\GihyoIkaLog\src\03>dist\hello_world.exe
hello world
wxPythonでGUI開発
もう1つの取り組みとして、IkaLogにGUIを付ける作業をはじめました。IkaLog実行中の操作をGUIでできるといいのでしょうが、何よりCLIなどでゲーマー層に設定ファイルを作成させるのは、かなり難易度が高い話です。このため、とりあえず設定や実行をWindowsアプリケーションっぽくすることを目指しました。
GUI部分の開発には、Windows、Mac OS X、そしてLinuxをターゲットにしたマルチプラットフォームなGUIアプリケーションを作成できるwxWidgets、それをさらにPythonから利用できるようにするバインディングであるwxPythonを利用することにしました。別件で、PythonとwxPythonの組み合わせでWindowsとMac OS Xの両方から利用できるGUIアプリが作れるか試していたため、「 これを使えば、とりあえず目的のものは作れるだろう」という感触を持っていたからです。
私は、高校生から大学生の頃にDelphiでWin32アプリを書いたりしていたので、wxWidgetsやwxPythonを用いたプログラミングはなんとなく感覚的にわかります。また、2004年の新卒当時、新人研修でJava AWTを使った開発を経験していたので、その感覚でwxPythonを使ったコードを書くことができました。
図 IkaLog GUI (IkaUI)
Windows向けの利用を想定したIkaLogのGUI機能ですが、その開発は基本的に「MacOS X上で開発および動作チェックを済ませたうえで、Windows上で最終的な動作確認をする」というフローで開発を進めました。IkaLogの画像認識部分などは、Mac OS X上で動けば、Windowsでもだいたい動きます。しかし、wxWidgetsやwxPythonがクラスプラットフォームを謳うGUIツールキットだといっても、「 Mac OS Xで動いても、Windowsで表示されない」「 Windowsで動いても、Mac OS Xだとエラーで落ちる」といった程度には挙動差が生じていました。IkaLogのような、wxWidgetsのお作法をよく理解せずに書かれたコードでは、他プラットフォーム上でバッチリ一発動作させるのは難しいのかもしれません。このため、GUI部分に変更を加えた際には、原則Windows環境で動作確認するようにしています。
IkaLogのGUI部分は旧来からのデスクトップアプリケーションのような作りになっていますが、今は「Webアプリケーションとして作るのが理想的だったかな」と思っています。たとえば、棒グラフや円グラフなどをユーザーに見せるためのライブラリはHTML5のほうが良いものが充実しています。また、手元で試しているかぎり、MacOS XでwxPythonを使ったGUIにビデオのフレームを描画するより、WebブラウザにMotion JPEGフォーマットでストリームしたほうがプロセッサへのの負荷が低いこともわかっています。そして、いまどきのUIに関わられているエンジニアの協力も得やすかったでしょう。
Windows版のビルド環境にはSlackでアクセス
先述のとおり、私は普段はMac OS Xで(IkaLogに限らず)ほとんどの作業をしており、Windows環境はあまり使っていません。しかし、IkaLogのWindows版バイナリを生成するには、Windows環境での作業が必要になります。
当初は、IkaLogのWindows版バイナリを生成するためだけに、MacBook Pro上でWindowsの仮想マシンを起動し、手作業でPy2EXEを実行していました。しかし、この作業は地味に面倒でした。自宅で作業しているときはまだいいのですが、年末年始は毎年スノーボード旅行で青森県八甲田山に遊びに出かけます。2015年年末は、ちょうどスノーボード旅行のタイミングでスプラトゥーンのアップデートがあり、山荘の布団の中からIkaLogの機械学習データの更新やWindows版バイナリの生成作業をしたのですが、これがまたけっこう大変でした。
このため、現在はIkaLog開発に使用しているSlackに/build
という専用のコマンドを追加 しています。このコマンドを実行すると、Slack→Jenkins→自宅にあるビルドサーバ(Windows 10の仮想マシン)へとジョブが伝わり、下記の作業が自動的に行われるようにして、省力化しています。
自動的にGitHubのmasterブランチをチェックアウト
Py2EXEでEXEファイルを生成
公開用ZIPファイルを作成
公開領域にZIPファイルをコピー
Slack経由で生成したZIPファイルのURLをフィードバック
結果として、( やろうと思えば、ですが)電車の中からiPhoneでパッチをマージ、Slackアプリから/build
コマンドを実行し、最新のWindows版IkaLogバイナリを生成できるようにしています。
図 SlackからIkaLogをビルド