【初心者向け】OpenCV, NumPyを使ってSplatoonの解析をしてみる

# :diamonds:　はじめに

僕はPythonを勉強し始めて数か月経った時に、Pythonの練習として何か作りたいなーと思っていました。
そこで、当時ハマっていたSplatoon2に関する解析システムを作りました。

この記事は、それに関してNotionで書いた↓の記事をもとに、より汎用的な内容に変えたものです。

https://www.notion.so/2-0e20b684d15f4edf8a5a7d7f01a29394

:::note
Pythonの練習をしたいけど何を作ればいいか分からない、、
:::
:::note
Pythonを使ってゲームの分析をすることでゲームでもっと勝てるようになりたい！
:::
といった方を対象として書いています。
実際これはプログラミングを始めてから半年も経っていないときに作っているので、あまり高度なことはしていないと思います。
ぜひこれを参考にして色々作ってみてください！

質問があればぜひコメントください！

# :diamonds: この記事を読んで知ることができること

- Pythonを使ってどんなことができるのか
- Pythonを使って画像をどうやって扱えばいいか
- Pythonを使ってNintendoSwitchなどのゲームの分析をする方法

# :diamonds: 必要なもの

## :spades: プログラム実行環境
|  環境  |  version  |
| ---- | ---- |
|  Windows 10  |  -  |
|  Python  |  3.10.6  |
|  opencv-python  |  4.5.5.64  |
|  numpy  |  1.22.3  |

:::note warn
細かく書いていますが、今回作るシステムでは環境による違いはあまりない気がします。
:::

## :spades: その他アプリやデバイス
- Nintendo Switch

https://www.nintendo.co.jp/hardware/switch/

- Splatoon2
 
https://www.nintendo.co.jp/switch/aab6a/index.html

↓[ChatGPT](https://openai.com/blog/chatgpt)にSplatoon2の概要を教えてもらいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/b674a0a5-4b73-4c7f-583c-4bab4740987e.png)

- OBS
画面を録画するのに使います。
これも録画できればなんでもいいです。
OBSの初期設定はここらへんの記事が参考になるかなと思います。

https://huhulog.net/game/switch-obs-setting/

- キャプチャーボード
Nintendo Switchの画面をPCに映すことができればなんでもいいです。
僕は安いやつを買いました。

https://www.amazon.co.jp/gp/product/B08KW6MQW8/ref=ppx_yo_dt_b_asin_title_o03_s00?ie=UTF8&psc=1

# :diamonds: 実装したいこと
Splatoon2は4対4の計8人のプレイヤーで行います。
1試合は3分のものと5分のものがあり、後者は時間が延長することもあります。
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/c4d61586-6f98-7a4d-798d-d2a4a57def5f.png)
これがゲーム画面で、画面上部を見ると、8人のプレイヤーの状況や、試合の残り時間を見ることができます。
今回実装したいことは、1試合の8人のプレイヤーの状況の推移を記録することです。
つまり、↓のようなデータを取得することを目標にします！

| 時間 | プレイヤー1 | プレイヤー2 | プレイヤー3 | プレイヤー4 | プレイヤー5 | プレイヤー6 | プレイヤー7 | プレイヤー8 | 
|-----|-----|-----|-----|-----|-----|-----|-----|-----|
| 0 | alive | alive | death | alive | SP | alive | alive | death |
| 1 | alive | alive | alive | alive | SP | alive | alive | death |
| 2 | death | alive | alive | alive | SP | alive | alive | alive |
| 3 | death | SP | alive | alive | SP | death | SP | alive |
| 4 | death | SP | alive | alive | alive | death | SP | alive |
| 5 | death | SP | alive | alive | alive | death | SP | alive |
| 6 | death | SP | alive | death | alive | death | SP | alive |
| 7 | death | SP | alive | death | alive | death | SP | alive |
...


# :diamonds: 方法

## :spades: 全体の流れ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/dd6b6e6a-61e0-9b57-edb7-6e876d559d79.png)
※図は[draw.io](https://chrome.google.com/webstore/detail/diagramsnet/onlkggianjhjenigcpigpjehhpplldkc?hl=ja)で作成しました。
全体の流れは上の図のような感じです。

## :spades: アイコンの座標を取得

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/5f23bf3d-b5d2-48f6-4bef-c05078e110f6.png)

この画像から、

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/18b5f18b-3a8e-5378-a294-aca254d7b025.png)

の部分を抜き取りたい。
そのためにまず、OpenCVを使ってアイコンの座標を取得します。

https://note.nkmk.me/python-opencv-videocapture-file-camera/

https://magazine.techacademy.jp/magazine/51035

このあたりが参考になるかなと思います。

## :spades: アイコンのトリミングをする

上で取得した座標をもとに、アイコンのトリミングをします。

https://qiita.com/ikanamazu/items/d752225a0a9834ce0d41

このあたりが参考になるかなと思います。

## :spades: 画像データを配列として読み取り、配列として画像を判定する
![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/18b5f18b-3a8e-5378-a294-aca254d7b025.png)![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/aeeda818-1f65-afc4-8cd3-4b50bc23f76f.png)![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/dc3fe964-18d4-f646-fec1-4591c69ee014.png)
トリミングしたアイコンの色をもとに、左の画像から、"alive", "death", "SP"と判定します。

"alive": 黄色が多い
"death": 彩度の低い白黒が多い
"SP": 白っぽい

ということが画像から読み取れます。

まず"alive"の判定をします。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/18b5f18b-3a8e-5378-a294-aca254d7b025.png)

https://rikoubou.hatenablog.com/entry/2019/02/21/190310

https://pystyle.info/opencv-image-binarization/

このあたりが参考になるかなと思います。

黄色だけを抽出した後、2値化をして、アイコンを0と1の2次元配列に変換します。
（黄色が1、それ以外が0）


次に"death"の判定をしていきます。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/aeeda818-1f65-afc4-8cd3-4b50bc23f76f.png)

彩度が低いという特徴を使います。
まず、画像の色をRGBからHSVに変換します。

https://qiita.com/sitar-harmonics/items/d864728e002f94872d97

そして、彩度を表すSの、その画像における平均を計算し、それがある一定値以下であれば"death"だと判定します。

https://kino-code.com/python-numpy-mean/

最後に"SP"を判定していきます。

![Untitled.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/dc3fe964-18d4-f646-fec1-4591c69ee014.png)

"SP"の時には白っぽい、つまり明度が高いということが読み取れたので、"death"のときと同じように、HSVに変換した後、明度Vの平均を計算して、それがある一定値以上であれば"SP"だと判定します。

さらにプレイヤーの状況の判定に加えて
- マップを開いているかどうかを判定する
- 試合が始まっているかどうかを判定する

の機能もつけていますが、ここでは省略します。

# :diamonds: 結果

いい感じに判定できてそうです。
アイコンの下の色とプレイヤーの状況の関係は下の通り。
- 赤： "alive"
- 黒： "death"
- 白： "SP"


結果1↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/f8cc92c4-8751-3b22-a5e2-6bb322b6bb80.png)

結果2↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2906464/1588925e-3d3b-8526-4421-3807d601b9e3.png)



# :diamonds: 今後していきたいこと
今回のシステムでは1試合の中の8人のプレイヤーの状態をデータとして取得しました。
今後はこのデータを機械学習にかけて、

- どういう試合の進み方だとその試合に勝ちやすいのか

を調べていけたらなと思います。

# :diamonds: まとめ
以上、OpenCV, NumPyを使ったSplatoonの解析プログラムについて解説しました。
作りたいものをベースとしてそのためにはどんなプログラムを書けばいいか、
つまり、
>「プログラミングは手段である」

ということを意識してこの記事を書きました。

そのため、今回のシステムでは簡単な技術しか使っていません。もっといい方法もあったんじゃないかなと今では思っています。
この記事を読んでいただいた方も、これを機会に知識を深めていって、より良い分析方法を探してみてください！
もし思いついたことがあったり質問があったりしたらコメントで教えてもらえると嬉しいです！

最後まで読んでいただきありがとうございました！


# :diamonds:実際に書いたプログラム

https://github.com/MRyutaro/splatoon_analysis

:::note warn
Splatoon2からSplatoon３への移行をしようとしたときにソースコードを少し変えたので、動かなくなっているかもしれません。。
ただ、プログラムコードの参考にはなると思います！
:::