Nwht0xn1

テンプレートマッチングでスプラトゥーンのゲーム結果画面を見ているかどうか判定してみたCreated on 2016-05-31 by r7kamura

画像処理の学習目的で、SquidAnalyzer という、スプラトゥーン の画面を解析するツールをつくりはじめた。実用途には、IkaLog というのが既にあるのでそちらを使うのがいいと思う。試合ごとの戦績を計算できるようになるところが目標ではあるものの、ひとまず初歩的な段階として、いま現在試合結果画面を見ているかどうかを判定する処理を実装することにした。画像処理自体の初学者であることから、更に難易度を下げて、最も単純なテンプレートマッチングを利用して (計算時間には拘らずに) 実装を行うことにした。

OpenCV

スプラトゥーンはWiiU専用のゲームなので、まずWiiUから画像を取得する必要がある。開発機としてMacbook Pro (Retina 13-inch Late 2013) を利用しており、今回は Intensity Shuttle for USB 3.0 を利用してHDMIとUSB 3.0経由でキャプチャ画像を取得することにした。

画像処理向けライブラリのOpenCVにHighGUIというモジュールが用意されており、様々なプラットフォームで動画像の読み込みを行う機能が提供されている。これを利用すると、OS XではAVFoundationの機能を利用してキャプチャ画像を取得できるようになる。後々Windows環境で動作できるようにすることも考慮して、この機能を利用することにした。

ruby-opencv

公式に提供されているOpenCV APIとして、C/C++用インターフェイスのほか、Java、Python、MATLABバインディングが存在するが、そのほかにも非公式の各種言語向けのラッパーが存在する。ruby-opencv はその1つで、ser1zwさんという方が、OpenCV 2系に対応させ、Ruby 2.0に対応させ…と着々と開発を続けておられるプロジェクト。Rubyに慣れているということや、ruby-opencvのコードがある程度読めるということもあって、今回はruby-opencvを利用することにした。先述したHighGUIモジュールの機能も、ruby-opencvを通して利用できる。

画像を読み込む

OpenCV::CvCapture.open を利用すると、動画像キャプチャのソースとなるWebカメラやキャプチャデバイス、あるいは動画ファイルなどにアクセスできるようになる。引数には、デバイスのインターフェースの種類を表す :ieee1394 などのSymbolや、動画ファイルへのパス、デバイスIDなどが指定できる。例えば自分の環境では、Macbook ProにIntensity ShuttleとThunderbolt Displayを接続しているので、デバイスIDとの対応が以下のような状態になっていた。

  • デバイスID 0: Blackmagic Intensity Shuttle
  • デバイスID 1: FaceTime HD Camera
  • デバイスID 2: FaceTime HD カメラ(ディスプレイ)

OpenCV::CvCapture.openOpenCV::CvCapture クラスのインスタンスを返すので、OpenCV::CvCapture#query インスタンスメソッドを呼び出すと、(次のフレームの) 画像を取得できる。ここまでを整理すると、以下のようなコードができる。

require "opencv"

device_id = 0
cv_capture = OpenCV::CvCapture.open(device_id)
cv_capture.query #=> #<OpenCV::IplImage:0x007fbc492270d8>

テンプレートマッチングで判定する

あらかじめ、特定のパターンを検出するための画像をテンプレートとして用意しておき、このテンプレートを用いて入力画像とのマッチングを行うことを、テンプレートマッチングと呼ぶ。テンプレートマッチングを行うことで、用意したテンプレートと同じパターンが画像中に存在するのかどうか、また存在したらどの位置にあるのかを知ることができる。テンプレートマッチングは、単純な方法でやると探索範囲に対してすべての位置で類似度を検索しないといけないので、はっきりいって処理時間が遅い。今回紹介する処理も、自分の環境では一度の探索に200ms程度要しているので、あまり速度が出ない。一方で、非常に簡単に実装できるので、まずはテンプレートマッチングでの判定から着手するということで今回採用してみた。

以下のような画像1をテンプレートとして利用して2

image

以下のような画像にテンプレートマッチングを適用させる。

image

また、スプラトゥーンでは試合ごとに味方・敵のインク色が変わるので、毎度完全に一致するわけではなく、以下のように配色が異なる場合も存在する。

image

テンプレートマッチングでは探索範囲ごとに類似度が得られるので、一度のテンプレートマッチングの適用結果の中で最も高かった類似度を取得し、その値がある閾値を超えた場合に一致したものとみなすことにした (例: 最高類似度が0.87を超えていれば一致)。

さて、これまでの内容を整理すると、以下のようなコードが出来上がる。

require "opencv"

device_id = 0
image = OpenCV::CvCapture.open(device_id)

template_image_path = "images/win.png"
template_image = OpenCV::IplImage.load(template_image_path)

cv_mat = image.match_template(template_image, ::OpenCV::CV_TM_CCORR_NORMED)
max_similarity_ratio = cv_mat.min_max_loc[1]

puts max_similarity_ratio > 0.87

ファイルパスから画像を読み込むために OpenCV::IplImage.load、テンプレートマッチングを行うために OpenCV::CvMat#match_template、最も高い類似度を得るために OpenCV::CvMat#min_mac_loc をそれぞれ利用している。テンプレートマッチングでは、入力画像とテンプレートとがどれだけ似ているかを計算する方法として、幾つかの種類があり、OpenCVでは以下の6種類が利用できる。上記のコードではその内の1つを指定している。

  • OpenCV::CV_TM_SQDIFF
  • OpenCV::CV_TM_SQDIFF_NORMED
  • OpenCV::CV_TM_CCORR
  • OpenCV::CV_TM_CCORR_NORMED
  • OpenCV::CV_TM_CCOEFF
  • OpenCV::CV_TM_CCOEFF_NORMED

それぞれの方式の計算式については、物体検出 — opencv 2.2 documentation などのOpenCVのドキュメントを見ると良い。自分が探した中では、パターンマッチング(正規化相関など) 画像処理ソリューション の説明が分かりやすかったが、正直紹介されている数式の本質は未だ理解できていない。今回 OpenCV::CV_TM_CCORR_NORMED を選択したのは、この方式が誤ったパターンでは値が高く、誤ったパターンでは値が低くなる性質がより顕著に見られたからだが、OpenCV::CV_TM_CCOEFF_NORMED でもそれほどの違いは見られなかった。

おわり

今回は非常に素朴なやり方であるテンプレートマッチングを利用したが、現実的な速度・精度で処理させるためには、高速化手法を取り入れたり、画像の特徴を利用した別の手法を用いる必要があると思う。また進捗があれば、引き続きこのコミュニティに何か投稿する予定です。


  1. 本記事に掲載されているスプラトゥーン関連画像は、任天堂株式会社の著作物からの引用です。 

  2. この辺が共通部分だろうなと思って適当に切り抜いて用意しました。