この記事の続き
前回は、ダーツ1本刺さった状態は95%を超える認識率で、良い感じだったのですが、2本3本の場合、同じところに2本3本刺さった場合(例えば20トリプルに3本とか…まずないけどw)の認識率を高める方法が中々見つからないままでした。
最初は出力64モデル型(各数字のシングル・ダブル・トリプル+インブル、アウトブル、アウトボード、何もなし)でしたが、複数ダーツに対応すべく、64x3=192(同じところに1本、2本、3本)モデル型で学習データを大量に作り学習させました。
結果は思わしくなく、このマルチクラスマルチラベル型モデルは断念しました。
同時にゲーム性を考慮すると、ダーツを1本投げる度に判定ボタンを押すのはイマイチな感じなので、Webカメラでライブ映像を撮影しながら、その映像の変化を捉えて「次のダーツが刺さった」ことを認識するロジックを検討しました。
これは比較的簡単で、前回判定に使った画像をベースとして、その画像との差分が一定以上ある画像に変化した場合に、次のダーツが刺さったとみなす、というものです。
そして、このロジックを組み立てているうちに、閃きました。
差分のマスク画像を作り、最新のダーツだけが残る画像を生成することで、1本64モデル型が活用できるじゃないか!ってことです。
最新ダーツのみの合成画像作成
その差分を各ピクセル毎に取得し、一定以上差があるピクセルを白、それ以外を黒に塗ると、このようなマスク画像が完成します。
比較用及びマスク画像作成コードはPIL使うとこうなるけど、これってcv2とかのnumpy形式だったら、Python達人はもっと簡単に比較できるコード書けそうw
mask = Image.new("L", small_img.size, 0)
width, height = mask.size
mse_cnt = 0
for y in range(height):
for x in range(width):
a = small_img.getpixel((x, y))
b = prev_img.getpixel((x, y))
c = (a[0] - b[0])**2 + (a[1] - b[1])**2 + (a[2] - b[2])**2
if c > mse_thd:
mask.putpixel((x, y), 255)
mse_cnt += 1
else:
mask.putpixel((x, y), 0)
最後に、何も刺さってないベース画像に最後の2本刺さった画像をマスク画像でマスクして合成するとこのように2本目だけの画像になります。
これで画像の変化のタイミング(=次のダーツを投げたタイミング)と、その差分マスクによる合成で最後のダーツのみの画像が取得できることになりました。
あとは、64モデルに投入して判定するだけです。
もちろん、通常の画像よりも、合成画像は認識率が落ちるため、誤判定も増えます。
この誤判定画像をフォルダに溜めておいて、追加学習に使うという魂胆です!あぁ美しい流れw
こんな感じで訂正した画像が、その位置を示すファイル名と共に蓄積されていく。
ViTモデルに限らずディープラーニングの良いところは、こんなざっくりしたマスク画像で、多少の残骸が残っていても、なんとか学習してくれるところ。ロジックだと、残骸データに振り回されて正解に辿り着かないことも多々あるはず。
試しに301ゲーム作ってみた
実際に投げているところを少しだけ動画でどうぞ。
https://youtu.be/ORm_dlB9giI
今後の展望
あとは、別の人の部屋など、照明やレイアウト、カメラ角度、ボード種類、ダーツなど微妙に異なる環境でファインチューニングできるかどうかで、使い物になりそうかどうかが決まりますね。
ソフトダーツは自動判定機器がそれなりに入手可能な価格で出回っているけど、ハードダーツはそうもいかないらしい。
ニーズはそれなりにあるかも。
必要な設備はPC(Windows)とWebカメラ。GPUは学習時に必要だから、ファインチューニングに時間をかけたくなければ必要。ゲームを楽しむときは1枚の画像判定なのでGPU無しでも十分利用できそう。
01ゲームは簡単に実装できたので、次はクリケット作るかな。
いや、その前にモデルの認識率を上げなきゃw
続く・・・w