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