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


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