Splatoon2の動画でPythonとOpenCVを使ってやられた場面集を作る

OpenCVのUnityアセットを購入していろいろいじっていたのですが、なんかよくわからなくなり、じゃあPythonの勉強(触ったことない)もかねてなんか作ってみようと思いました。

なぜPythonかというと興味があったし、OpenCVと言えば!みたいなイメージがありました。

環境はあちこちでオススメされていたpyCharmを使います。普段PHPStormを使っているというのも理由です。

内容としては、普段スプラトゥーン2を録画しているので、自分がやられたときを認識し、「こうやってやられたぞ集」を作ります。

まず、やられたを認識するわけですが、

最初はこの画面中央のやられた!を認識(matchTemplate)させようと思っていましたが、ここは拡大縮小のアニメーションがあるので認識としてはどうかな?ということと、OpenCVfotUnityでまったく認識しなかったという苦い思い出があるので、右下の「復活まで」を使うことにします。

これです。

ではコードはこちら

#画像をグレースケールで読み込む
img = cv2.imread("splatoonimage.png", 0)
temp = cv2.imread("template.png", 0)
#マッチングテンプレートを実行
#比較方法はcv2.TM_CCOEFF_NORMEDを選択
result = cv2.matchTemplate(img, temp, cv2.TM_CCOEFF_NORMED)

#検出結果から検出領域の位置を取得
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
top_left = max_loc
w, h = temp.shape[::-1]
bottom_right = (top_left[0] + w, top_left[1] + h)

#検出領域を四角で囲んで保存
result = cv2.imread("splatoonimage.png")
cv2.rectangle(result,top_left, bottom_right, (255, 0, 0), 2)
cv2.imwrite("result.png", result)

実行してみます。

復活までのところに青い枠ができ、ちゃんと認識しています。

 

では動画の場合はというと、1フレームづつ取得して同じことをします。

# opencvのmachiTemplateで画像比較
def match(img , temp):
#比較方法はcv2.TM_CCOEFF_NORMEDを選択
 result = cv2.matchTemplate(img, temp, cv2.TM_CCOEFF_NORMED)
#結果のmax_valが欲しい 0-1 1に近いほど似てる
 min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
 return (max_val)
#ここから
video = cv2.VideoCapture(file_name)
frame_count = int(video.get(7)) #フレーム数を取得
frame_rate = int(video.get(5)) #フレームレート(1フレームの時間単位はミリ秒)の取得 splatoon2は60
deadTime = 0 # 前回のチェックとの比較時間用。近いフレームは無視するようにする
dead = [] # 取得タイミングをリストに入れる。秒単位
n=3 #n秒ごと

for i in range(int((frame_count / frame_rate)/n)): #動画の秒数を取得し、回す
    video.set(1 ,frame_rate * n * i);
    _, frame = video.read() #動画をフレームに読み込み
    framegray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #比較用にグレー画像を作る
    checkVal = match(framegray,templateStart); #関数呼び出し

動画を3秒ごとに画像として取得しています。すべてのフレームを取得すると1秒間に60枚。3秒とすると180枚なので、処理が1/180で済みます。ただし、正確な時間ではなくなります。

matchという関数が本体です。フレーム画像と比較画像(Template)をmatchTemplateというOpenCVの関数に渡しています。まったく同じなら1、結構同じなら0.8くらい。まったく違うのは0という値で返ってきます。

ではその値で時間を割り出します

    if match(framegray,templateDead) > 0.8 and deadTime < (i-1) *n - 10 :
        deadTime = (i-1) * (n)
        print ("deadTime : "+str(deadTime))
        dead.append(deadTime);

値は0.8以上で直近10秒以内ではない(3秒ごとなので続けて認識する場合もある)場合にdeadのリストに秒数を追加します。

 

これで動画内で何秒にやられたかの秒数が取得できました。

if len(dead)>0 :
    #動画作成
    fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
    newVideo = cv2.VideoWriter('deadonlyvideo.m4v', fourcc, frame_rate, (1920, 1080))
    for i in dead:
        sFrame = i * frame_rate - frame_rate * 4; #n秒前
        eFrame = i * frame_rate + frame_rate * 2;
        video.set(1 ,sFrame);
        for no in range(sFrame,eFrame):
            _, frame = video.read()
            newVideo.write(frame)

やられた秒数の前4秒から後ろ2秒のフレームを動画に書き込んでいきます。

これでできたのがこちら

Pythonでフレームを繋げて動画にしていますので音がないです。それと、開始と終了もいれるようにしました。

へたくそ?えぇ自覚しています。でもこれでもエリアでS+までいったんですよ(いまはSだけど。。。)

OpenCVもPythonも面白いですね。

この仕組みの延長でいろいろできそうです。

1、ffmegを使って動画を作ることで音を入れる

2、倒した場面も取れる

3、いろんな統計をとったりする

4、リアルタイムで取得する

5、アプリ化

とかできると面白いかも。

 

あ、フレンド申請気軽にどうぞ。

フレンドコード SW-2476-8687-7293

1件のコメント

  1. ピンバック:Splatoon2である意味チート的なものを作ってみた | NAOMO

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です