🔍

YOLOによる小物体検出 - 過学習とフレーム差分で精度向上

に公開

はじめに

「キャップ野球」は、ペットボトルのキャップをボールの代わりに使用するユニークなスポーツです。私はこの競技のデータプラットフォームを開発・運営しており、試合成績の記録/閲覧や選手分析などの機能を提供しています。

https://cap-baseball.com

野球では球場に固定されたハイスピードカメラなどによる分析が進んでおり、分析されたデータは様々なところに使われていると聞きます。
キャップ野球もリーグ戦や大会などが数多く行われ、ハイスピードカメラではないものの映像記録は豊富に残っており、その映像記録からデータ分析を行えないか考えています。

本業はフロントエンドエンジニアでpythonはあまり詳しくないのですが、YOLOによる動画からの物体検出にチャレンジしてみました。

今回のリポジトリ

今回のソースコードはこちらになります。

https://github.com/koshien2015/ultralytics

Google colaboratory ノートブック

https://colab.research.google.com/github/koshien2015/ultralytics/blob/cap_detection/yolo_tracking_colab.ipynb

使用技術

  • YOLO v8(ultralytics)
  • Docker Compose
  • GPU(GeForce RTX 3060)

導入に際しての工夫

基本的なセットアップ手順は公式を参照
https://docs.ultralytics.com/ja/quickstart/

公式チュートリアルでは docker runコマンドを使っていますが、慣れているdocker composeで起動したかったので、compose.ymlを作りました。

version: '3.7'
services:
  yolov8:
    image: ultralytics/ultralytics:latest
    container_name: yolo_container
    ipc: host
    volumes:
      - ../shared:/shared
    ports:
      - "6006:6006"
    environment:
      - DISPLAY=:1
    tty: true
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              capabilities: [gpu]
              count: all

これで docker compose upでGPUに対応したコンテナを起動することができ、コンテナの中に入ってpythonスクリプトを実行するだけで手軽に解析ができるようになります。

アノテーション

詳細なアノテーションの手順は基本的なセットアップ手順は公式を参照願います。

  1. aviutlなどで動画をフレームで連番出力
  2. LabelImgというソフトを使って、フレーム(画像)に物体のラベリングをしていく

という手順で、アノテーションがついた画像を用意して学習させます。

蓋だけじゃないアノテーション

まず、今回の主目的は「蓋」(ペットボトルキャップ)なので、capというアノテーションのほか、

  • 投手が投球モーション中
  • 投手がリリースを終えた後のモーション
  • 打者が待球姿勢
  • 打者がスイングをかけた時の姿勢
  • 捕手が構えた姿勢
  • 捕手が捕球したときの姿勢
  • 主審

などをアノテーションとして付与し、打者のスイングのタイミングや投手のリリースのタイミングなどは一般的なモーションではほぼ正確に検出できるようになっています。


アノテーションをする段階で色々な知見を得られました。

バットも含めてアノテーションすべきか?

  • フォーム(構えやスイング)の検出: 構え・スイング時のポーズの一部としてバットが重要なので「含める」

キャップが「手と同化する場面」を除外すると精度が上がる

  • YOLOは見た目の一貫性を重視します
  • capのラベルが「ときどき手と一体化している」「ときどき空中にある」ような場合、モデルが「capとは何か」の判断に混乱する
  • capを見つけるべきタイミングは 「リリース後(空中)」
  • モーション中はそもそも見えてないか、見えても小さいorブレてる
  • capが「空中に浮いてる状態の画像」を重点的に

モデルの学習

小さな物体の検出精度を上げるのは難しい

  • 「キャップ」が小さいから1〜2ピクセルのズレが学習に大打撃
  • 正解ラベルがあっても 位置やサイズが微妙にバラついてると精度が伸びない
  • 似たようなシーンだけだとモデルが「このパターンでしか出てこない」と思ってしまう
  • キャップがフレームごとに微小・高速移動してる
  • 背景とキャップのコントラストが低い(白いキャップ vs 空や壁)
  • モーションブラーが入りやすい
  • オブジェクトサイズが非常に小さい(YOLO系が苦手なパターン)

「ラベルがない画像」を学習に含めた場合の挙動

  • 「この画像には検出対象がいない」として学習する
  • モデルはそれを「背景データ」として扱う
  • ラベルをつけていない画像に検出対象がある場合は精度が下がってしまう
  • 背景データにしない場合は必ずラベルをつけた画像を使う

転移学習を使う

  • 最初からYOLOを1から学習するのではなく、学習済みモデルから微調整する
  • 今回はYOLO8mをベースモデルに使用
  • 学習させて、結果が良ければモデルを保存して次の学習へ引き継ぐ(追記学習)

学習における試行錯誤

汎化性能が上がらない

モデルの学習初心者だったので、初めの方は

  • トレーニングデータ(train)
  • 検証データ(val)
  • テストデータ(test)

に同じ画像/アノテーションセットを放り込んで「検出精度が高い!」と喜んでいたのですが、実はそれは「過学習」と呼ばれるもので、学習データだけに最適化されてしまって汎用性がない状態に陥っていました。途中からモデルを汎用化させることに方針を変えてみましたが、素材的に難易度が高いこともあり、検出精度が上がらない状態が続きました。

mAP50-95が0.42付近で頭打ち

このあたりでファインチューニングを諦めて、検出精度の良かった過学習へと方向転換しました。

過学習

学習データだけに最適化されてしまって汎用性がない状態

メリット

  • ラベルの一貫性があれば、過学習でも検出精度は「実用上」高くなることがある。
  • 同一環境 × 限定用途で使いたい場合は過学習気味のモデルでOK。検出率重視
  • データ数が少なくても、比較的少ない学習(エポック)回数で収束する

デメリット

  • 新しい環境・画角・明るさでは一気に破綻する。
  • 別会場 / 他人の動画では急に検出できなくなる・全く別の場所を拾う

過学習と追記学習で、7つの体育館で約700枚の画像(フレーム)を使って、キャップは検出できるものの、誤検出も混ざる状態になりました。
ここまでで、小さくて白い円盤状の物体の検出はできたことになります。

フレーム差分から動体を強調する

動画のフレーム差分から動体を検出して輪郭を強調するフィルタを前処理として入れます。
こちらの記事を参考にさせていただきました。
https://qiita.com/otakoma/items/04216c60fa31eae60947

すると、小さくて白い円盤状の物体から動く小さくて白い円盤状の物体へ検出対象をフィルタすることができ、誤検出を減らすことができ、検出精度が上がりました。

まとめ

限定的な用途であれば、過学習 + 前処理フィルタ で小さい物体の検出精度を上げることができます。

今後の課題

検出率が高くない会場(体育館)は追記学習しないといけない。

今後の展望

  • 投手のリリースタイミング、捕手の捕球タイミングを検出する
    • 動画から自動的に球速を算出できる
  • キャップの軌道を追尾する
    • 深度(奥行き)情報がないため 3 次元への変換は難しいが、時間経過を軸にある程度 3 次元の軌道を復元できる
    • 打席結果しかデータがない打席でも、投球コース情報などを自動的に算出できる
  • 捕手の守備時の移動距離を計算する
    • 投手のコマンド力を評価できる(意図したところにいったかどうか)
    • 捕手のブロッキング力を評価できる(どれぐらい動けるか)
    • 捕手のゾーン別の得手不得手を可視化できる

検出結果の動画

https://youtu.be/b6lDOLyeqdc

Discussion