お気持ち表明のブログ

ツイッターで書けないことを書こうと思います

ギルティの体力とテンションゲージの時間変化をグラフにするやつを自分もやってみたいと思ったからプログラム初心者が挑戦している話。その2

あいかわらず乱文。めちゃ読みにくい。技術的にも初心者の文。

結果だけが知りたい人は結果だけ見てください。



最初に

こんにちは。椎名りるあです。

この記事は前回の記事、ギルティの体力とテンションゲージの時間変化をグラフにするやつを自分もやってみたいと思ったからプログラム初心者が挑戦している話。その1の続きです。

ri-ru-ria.hatenablog.com

その1を読んでからのほうがいいかもしれません。

この文章も初心者のガバ文なので注意してください。

さて、その1から多少の更新があったのでその点を記しておきます。

そのため、内容はあっさりとしています。完全に自分用備忘録。

(なお、1週間以上前に内容は更新していたのですが、面倒でまとめていませんでした)


グラフのノイズ除去に対する処理

まずはノイズ除去に対しての処理を変更しました。

以前は、読み込んだ画像に対しての「メディアンフィルタ

得られたグラフに対しての「ローパスフィルタ」

を組み合わせていましたが、ローパスフィルタを使うと、どうしても時間軸0の点が実際と合わないので廃止しました。

代わりに、シンプルな「移動平均」を用いました。

移動平均だと、グラフの精度が下がる恐れがありましたが、冷静になると体力ゲージやテンションゲージって超正確に知る必要がないんですよね。

なので、移動平均法を採用しました。

algorithm.joho.info

コードは以下になります。

    # 移動平均の点数
    n = 3
    
    # 配列生成
    data = np.array([ 1,  2,  5,  4,  2,  4,  3])
    
    # コンボリューション積分で移動平均の計算
    ave = np.convolve(data, np.ones(n)/float(n), 'same')

mode='same'にすることで結果も同じ点数にします。ノイズの処理はこれだけです。

実際は30点というかなりの範囲で移動平均を取りましたが、見た目上の問題はなかったです。


バーストの検知

今回の記事のメインです。

どのようにバーストを検知するかですが、以下の方法を取りました。

1.試合開始時のバーストゲージの情報を保存

2.試合中のバーストゲージを試合開始時のものと比較

3.急に変化したところをバーストを使用したところと定義し、感知。

この3ステップです。

1.試合開始時のバーストゲージの情報を保存

まずバーストゲージだけを抽出します。

やることはHPゲージやテンションゲージと変わらないので割愛。

注意点としてはinrangeで切り取る色領域はバーストゲージが青でも赤でも読み取れるように調整することです。

次に試合開始時のバーストゲージの保存です。

私は次のように実装しました。 dev.classmethod.jp

ここに書いてあったのをまねしただけです。

avg_bg = None #バーストゲージ開始時のデータ

if avg_bg is None:
                avg_bg = burst.copy().astype("float") #空ならそのフレームの情報を保存
                

これで完了です。

2.試合中のバーストゲージを試合開始時のものと比較

dev.classmethod.jp

これもこのサイトを参考にしました。以下のように実装しました。

cv2.accumulateWeighted(burst, avg_bg, 3000)
frameDelta_bg = cv2.absdiff(burst, cv2.convertScaleAbs(avg_bg))
thresh_bg = cv2.threshold(frameDelta_bg, 100, 255, cv2.THRESH_BINARY)[1]

1行目は移動平均を出しているらしいですが、これいりますかね…?うーんわからん。

とりあえずいろいろ試したら3000とかいうバカでかい値だと一番きれいな出力だったので採用。

2行目で蓄積したフレームと保存したフレームの差分を出します。

3行目でもう1回2値化しました。これいらないですね。まま、ええやろ!

つづいて、差があった部分のピクセル数を数えます。コードは以下です。

whitePixels_bg = np.count_nonzero(thresh_bg)

するとこれが満タンのバーストゲージとの差の量になります。

3.急に変化したところをバーストを使用したところと定義し、感知。

得られた差の量のノイズを消した後、ある一定量以下のものを0にします。

これは後の処理を確実にするための前処理です。

threshs_bg = np.where(threshs_bg<300,0,threshs_bg)

np.whereを使って、300未満の値を0にしました。

変化を急峻にする目的もありますが、ノイズ除去も兼ねています。

次に急峻な変化をchanngeFinderを用いて検出します。

qiita.com

実装したコードはこんな感じ。

     cf = changefinder.ChangeFinder(r=0.003, order=1, smooth=3)        
     ret = []
     for i in threshs_bg1:
           score_bg = cf.update(i)
           ret.append(score_bg)
     ret = np.array(ret)

これで変化点が検出できます。

あとはこの変化点のピークを見ればいいわけですね。

ピーク発見には次の関数を使いました。

maxid_bg = signal.argrelmax(ret, order=1500) 

配列にはピーク位置がすべてが入るので、これでバーストゲージが検出できました。

ちなみにorder=1500はピークサーチの間隔に対応していて、ノイズによるバーストの誤検知を防いでいます。

あとはこれをプロットすればよいわけです。

4.結果

結果がこちら。検証のために2つの動画を読み込んでグラフ化しました。

f:id:ri_ru_ria:20201210232827p:plain
グラフ1
f:id:ri_ru_ria:20201210232957p:plain
グラフ2

1つ目のグラフの2P側でバーストを3回打っていますが、これは宝箱が重なったためにバーストを使用したと判断しためです。

これは避けられないと判断しました。今後の課題ですね。

2試合分のデータしかありませんが、分析っぽいことをしてみようと思います。

両試合とも2Pが負けているのですが、負けパターンとしては、

ゲージを使っていない。

ゲージを試合中盤で一気に使って後半で枯渇する

の2パターンで負けてしまっているきがします。結構情報が読み取れそうですね。面白いです。




今後ついて

バーストタイミングも検知できて、一旦それっぽくはなったのですが、課題としてはやはりノイズですね。

これは(当然)Numpyで実現できそうなのですがPandas使った方が楽そうかもと思ったりして悩んでします。教えてエロい人。

次に、ラウンド間の感知ですね。

これはできれば宝箱によるバーストの誤検知も防げるので実装したいですね。

どうやったらいいのか全く分かりませんが。

プログラミングほぼやったことない自分でも1週間ちょいでそれっぽいものができるのは間違いなくネットのおかげでしょう。ネットすごい。

自分が書いたコードは恥ずかしいので載せません。

以上、ほぼ備忘録でガバ文でしたが、読んでいただきありがとうございました。