Nintendo Switchから投稿された動画に自分を探す(2/4)


https://qiita.com/floatnflow/items/2b3f725d7e642ffe183b
Nintendo Switchから投稿された動画に自分を探す(1/3) の続き。

キル表示の切り出し

目的の動画は保存したので、ここからキル表示を切り出します。キル表示の仕様を調べてみると、

  • 位置は固定で最大4個
  • 時間は倒した相手一人につき約5秒
  • 複数表示時は一番下が最新 (古いものは上に繰り上げられる)

なので、動画を5秒に1回、固定箇所をキャプチャすれば取り漏れはないはずですが、動画の開始時、終了時や、マップ表示時など、必ずしも5秒間表示され続けている保証はないので、今回は1秒ごと、取ってきた動画は30FPSなので30フレームごとに切り出します。

動画のキャプチャにはOpenCVを使います。OpenCVのVideoCaptureはread()でnumpy.ndarrayを返します。OpenCVでは基本的に画像を2次元(行x高さ)ないし3次元(+色)のnumpy.ndarrayとして扱います。

画面全体の左上の座標を(0, 0)としたときの、切り出し位置の左上と右下の座標を(x0, y0, x1, y1)として4箇所定義します。

import cv2

FPS = 30
capture_every = FPS * 1

killboxlocs = (
    (550, 662, 769, 693),
    (550, 618, 769, 649),
    (550, 574, 769, 605),
    (550, 530, 769, 561))

cap = cv2.VideoCapture('hoge.mp4')
while True:
    ret, frame = cap.read()
    if ret:
        count += 1
        if count%capture_every == 0:
            for loc in killboxlocs:
                img = frame[loc[1]:loc[3],loc[0]:loc[2]]

これで219x31の画像を1秒につき4枚切り出しました。ある時間の4枚の画像はこんな感じ。(すみません、たいせいさん、えちぜんさん、お名前お借りします。)




(画像の上下marginデカ過ぎ~ ;;)
全部使ってもいいんですが、後の文字認識処理はけっこう重いので、明らかにキル表示でないものは捨てます。背景が完全に黒だと楽なのですがそうでもないみたいなので、今回は一番上の行と一番下の行の輝度が高いものは捨てることにします。やり方や値は思いつきで、要チューニング。

def iskill(img):
    '''
    kill表示位置から切り出した画像がkill表示かどうかを判定する
    厳し過ぎると検出漏れ、甘過ぎると誤検出するためチューニングが必要
    '''
    if (img[0].max() > 90 or img[-1].max() > 90) and (img[0].mean() > 70 or img[-1].mean() > 70):
        return False
    else:
        return True

動画によってはこれでもかなりノイズ(キル表示でないもの)が混じります。(特にサーモンランとか)

また、時間経過による表示の変化はこんな感じ。




ボケた表示から始まって、一番最後が一番クッキリしてますが、それも動画によってはあまり一定していません。最終的にはこれのおかげで結果が揺らぎます。(めんどくせー)

そんなわけで、30秒間の動画なら最大30*4=120枚のキル表示を文字認識していくわけですが、

Tesseract-OCRで文字認識

とりあえず深く考えず、最新のTesseractをインストールします。現在の最新は4.0.0-RC3です。ちなみに今回の私の環境はWindowsです。あと、日本語の学習データ(jpn.traineddata)と、PythonからTesseractを操作するために、pyocrも入れます。

もしPythonからの実行時に、

unknown command line .. -psm

というエラーになったときは、

C:\Users\hoge\AppData\Local\Programs\Python\Python36-32\Lib\site-packages\pyocr
とかに入っている tesseract.py の167行目あたりの

return "--psm" if version[0] > 3 else "-psm"

return "--psm" if version[0] > 3 else "--psm"

に修正します。さて、やってみましょう。
あ、文字認識のimage_to_string()は引数にPillow Image型を取るのでfromarray()で変換を忘れずに。

import pyocr
from PIL import Image

tool = pyocr.get_available_tools()[0] # tesseしか入れてないので決めうち
builder = pyocr.builders.TextBuilder(tesseract_layout=6)
kill_text = tool.image_to_string(Image.fromarry(img), lang='jpn', builder=builder)

さぁ、果たしてちゃんと認識してくれるのか!?

またまた長くなったので続きはまた今度。

続きはこちら。>https://qiita.com/floatnflow/items/d0accb0689a51b8b01b0