成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

基于 Kalman 濾波的無標簽網球運動員追蹤

開發
本項目展示了如何利用GroundingDINO的無監督能力來追蹤網球運動員,而無需依賴標注數據,將復雜的目標檢測轉化為可操作的運動員追蹤。

近年來,隨著體育追蹤項目的興起,越來越多的體育愛好者開始使用自動化運動員追蹤技術。大多數方法遵循一個常見的工作流程:收集標注數據,訓練YOLO模型,將運動員坐標投影到場地或球場的俯視圖中,并利用這些追蹤數據生成高級分析,以獲取潛在的競技洞察。然而,在本項目中,我們提供了一種工具,可以繞過對標注數據的需求,轉而依賴GroundingDINO的無監督追蹤能力,并結合Kalman濾波來克服GroundingDINO輸出的噪聲問題。

我們的數據集來源于一組公開的廣播視頻,視頻鏈接:https://github.com/HaydenFaulkner/Tennis。這些數據包括2012年溫布爾登奧運會期間多場網球比賽的錄像,我們重點關注了塞雷娜·威廉姆斯(Serena Williams)和維多利亞·阿扎倫卡(Victoria Azarenka)之間的一場比賽。

對于不熟悉GroundingDINO的人來說,它將目標檢測與語言相結合,允許用戶輸入提示,例如“一個網球運動員”,模型隨后會返回符合描述的候選目標檢測框。RoboFlow提供了一個很好的教程,供有興趣使用的人參考——但我在下面也粘貼了一些非?;A的代碼。如下所示,你可以通過提示讓模型識別一些在目標檢測數據集中很少被標記的對象,比如狗狗和狗的舌頭。

from groundingdino.util.inference import load_model, load_image, predict, annotate

BOX_TRESHOLD = 0.35
TEXT_TRESHOLD = 0.25

# processes the image to GroundingDino standards
image_source, image = load_image("dog.jpg")

prompt = "dog tongue, dog"
boxes, logits, phrases = predict(
    model=model, 
    image=image, 
    caption=TEXT_PROMPT, 
    box_threshold=BOX_TRESHOLD, 
    text_threshold=TEXT_TRESHOLD
)

然而,在職業網球場上區分運動員并不像提示“網球運動員”那么簡單。模型經常會錯誤識別場上的其他人員,例如線審、球童和其他裁判,導致標注跳躍且不一致。此外,模型有時甚至無法在某些幀中檢測到運動員,導致標注框出現空白或無法持續出現在每一幀中。

追蹤在第一例中捕捉到線審,在第二例中捕捉到球童。圖片由作者制作

為了解決這些挑戰,我們應用了幾種有針對性的方法。首先,我們將檢測框縮小到所有可能框中的前三個概率最高的框。通常,線審的概率得分高于運動員,這就是為什么我們不只過濾到兩個框。然而,這引發了一個新問題:如何在每一幀中自動區分運動員和線審?

我們觀察到,線和球工作人員的檢測框通常持續時間較短,往往只持續幾幀。基于此,我們假設通過關聯連續幀中的框,可以過濾掉那些只短暫出現的人員,從而隔離出運動員。

那么,我們如何實現跨幀對象之間的這種關聯呢?幸運的是,多目標追蹤領域已經對這個問題進行了廣泛研究。Kalman濾波器是多目標追蹤中的主要工具,通常與其他識別指標(如顏色)結合使用。對于我們的目的,一個基本的Kalman濾波器實現就足夠了。簡單來說(更深入的探討可以參考這篇文章),Kalman濾波器是一種基于先前測量結果概率估計對象位置的方法。它在處理噪聲數據時特別有效,但也適用于在視頻中跨時間關聯對象,即使檢測不一致(例如運動員未被每一幀追蹤到)。我們在這里實現了一個完整的Kalman濾波器,但將在接下來的段落中介紹一些主要步驟。

二維Kalman濾波器的狀態非常簡單,如下所示。我們只需要跟蹤x和y位置以及對象在兩個方向上的速度(忽略加速度)。

class KalmanStateVector2D:
    x: float
    y: float
    vx: float
    vy: float

Kalman濾波器分為兩個步驟:首先預測對象在下一幀中的位置,然后根據新的測量結果(在我們的案例中來自目標檢測器)更新預測。然而,在我們的示例中,新幀可能會有多個新對象,甚至可能會丟失前一幀中存在的對象,這就引出了如何將之前看到的框與當前看到的框關聯起來的問題。

我們選擇使用馬氏距離(Mahalanobis distance)結合卡方檢驗來評估當前檢測與過去對象匹配的概率。此外,我們保留了一個過去對象的隊列,以便擁有比一幀更長的“記憶”。具體來說,我們的記憶存儲了過去30幀中看到的任何對象的軌跡。然后,對于我們在新幀中找到的每個對象,我們遍歷我們的記憶,找到最可能與當前對象匹配的先前對象,匹配概率由馬氏距離給出。然而,我們也可能看到一個全新的對象,在這種情況下,我們應該將一個新對象添加到我們的記憶中。如果任何對象與記憶中的任何框的關聯概率小于30%,我們將其作為新對象添加到記憶中。

完整的Kalman濾波器如下:


from dataclasses import dataclass

import numpy as np
from scipy import stats

class KalmanStateVectorNDAdaptiveQ:
    states: np.ndarray # for 2 dimensions these are [x, y, vx, vy]
    cov: np.ndarray # 4x4 covariance matrix

    def __init__(self, states: np.ndarray) -> None:
        self.state_matrix = states
        self.q = np.eye(self.state_matrix.shape[0])
        self.cov = None
        # assumes a single step transition
        self.f = np.eye(self.state_matrix.shape[0])
        
        # divide by 2 as we have a velocity for each state
        index = self.state_matrix.shape[0] // 2
        self.f[:index, index:] = np.eye(index)

    def initialize_covariance(self, noise_std: float) -> None:
        self.cov = np.eye(self.state_matrix.shape[0]) * noise_std**2

    def predict_next_state(self, dt: float) -> None:
        self.state_matrix = self.f @ self.state_matrix
        self.predict_next_covariance(dt)

    def predict_next_covariance(self, dt: float) -> None:
        self.cov = self.f @ self.cov @ self.f.T + self.q

    def __add__(self, other: np.ndarray) -> np.ndarray:
        return self.state_matrix + other

    def update_q(
        self, innovation: np.ndarray, kalman_gain: np.ndarray, alpha: float = 0.98
    ) -> None:
        innovation = innovation.reshape(-1, 1)
        self.q = (
            alpha * self.q
            + (1 - alpha) * kalman_gain @ innovation @ innovation.T @ kalman_gain.T
        )

class KalmanNDTrackerAdaptiveQ:

    def __init__(
        self,
        state: KalmanStateVectorNDAdaptiveQ,
        R: float,  # R
        Q: float,  # Q
        h: np.ndarray = None,
    ) -> None:
        self.state = state
        self.state.initialize_covariance(Q)
        self.predicted_state = None
        self.previous_states = []
        self.h = np.eye(self.state.state_matrix.shape[0]) if h is None else h
        self.R = np.eye(self.h.shape[0]) * R**2
        self.previous_measurements = []
        self.previous_measurements.append(
            (self.h @ self.state.state_matrix).reshape(-1, 1)
        )

    def predict(self, dt: float) -> None:
        self.previous_states.append(self.state)
        self.state.predict_next_state(dt)

    def update_covariance(self, gain: np.ndarray) -> None:
        self.state.cov -= gain @ self.h @ self.state.cov

    def update(
        self, measurement: np.ndarray, dt: float = 1, predict: bool = True
    ) -> None:
        """Measurement will be a x, y position"""
        self.previous_measurements.append(measurement)
        assert dt == 1, "Only single step transitions are supported due to F matrix"
        if predict:
            self.predict(dt=dt)
        innovation = measurement - self.h @ self.state.state_matrix
        gain_invertible = self.h @ self.state.cov @ self.h.T + self.R
        gain_inverse = np.linalg.inv(gain_invertible)
        gain = self.state.cov @ self.h.T @ gain_inverse

        new_state = self.state.state_matrix + gain @ innovation

        self.update_covariance(gain)
        self.state.update_q(innovation, gain)
        self.state.state_matrix = new_state

    def compute_mahalanobis_distance(self, measurement: np.ndarray) -> float:
        innovation = measurement - self.h @ self.state.state_matrix
        return np.sqrt(
            innovation.T
            @ np.linalg.inv(
                self.h @ self.state.cov @ self.h.T + self.R
            )
            @ innovation
        )

    def compute_p_value(self, distance: float) -> float:
        return 1 - stats.chi2.cdf(distance, df=self.h.shape[0])

    def compute_p_value_from_measurement(self, measurement: np.ndarray) -> float:
        """Returns the probability that the measurement is consistent with the predicted state"""
        distance = self.compute_mahalanobis_distance(measurement)
        return self.compute_p_value(distance)

在追蹤了過去30幀中檢測到的每個對象后,我們現在可以設計啟發式方法來精確定位哪些框最有可能代表我們的運動員。我們測試了兩種方法:選擇最靠近底線中心的框,以及選擇在我們記憶中觀察歷史最長的框。經驗上,第一種策略在實際運動員遠離底線時經常將線審標記為運動員,使其不太可靠。與此同時,我們注意到GroundingDino往往在不同的線審和球童之間“閃爍”,而真正的運動員則保持相對穩定的存在。因此,我們的最終規則是選擇記憶中追蹤歷史最長的框作為真正的運動員。正如你在初始視頻中看到的,對于如此簡單的規則來說,它的效果出奇地好!

現在,我們的追蹤系統已經在圖像上建立,我們可以轉向更傳統的分析,從鳥瞰視角追蹤運動員。這種視角可以評估關鍵指標,例如總移動距離、運動員速度和球場位置趨勢。例如,我們可以分析運動員是否經常根據比賽中的位置針對對手的反手。為了實現這一點,我們需要將運動員坐標從圖像投影到標準化的球場模板上,從上方對齊視角以進行空間分析。

這就是單應性變換(homography)發揮作用的地方。單應性描述了兩個表面之間的映射關系,在我們的案例中,這意味著將原始圖像中的點映射到俯視的球場視圖。通過在原始圖像中識別一些關鍵點(例如球場上的線交叉點),我們可以計算一個單應性矩陣,將任何點轉換為鳥瞰圖。為了創建這個單應性矩陣,我們首先需要識別這些“關鍵點”。RoboFlow等平臺上的各種開源、許可寬松的模型可以幫助檢測這些點,或者我們可以在參考圖像上手動標記它們以用于變換。

正如你所看到的,預測的關鍵點并不完美,但我們發現小的誤差對最終的變換矩陣影響不大

在標記這些關鍵點后,下一步是將它們與參考球場圖像上的對應點匹配,以生成單應性矩陣。使用OpenCV,我們可以用幾行簡單的代碼創建這個變換矩陣:

import numpy as np
import cv2

# order of the points matters
source = np.array(keypoints) # (n, 2) matrix
target = np.array(court_coords) # (n, 2) matrix
m, _ = cv2.findHomography(source, target)

有了單應性矩陣,我們可以將圖像中的任何點映射到參考球場上。對于這個項目,我們的重點是運動員在球場上的位置。為了確定這一點,我們取每個運動員邊界框底部的中心點,將其作為他們在鳥瞰圖中的球場位置。

我們使用框底部的中心點來映射每個運動員在球場上的位置。圖示顯示了通過我們的單應性矩陣將關鍵點轉換為鳥瞰圖中的網球球場

總之,本項目展示了如何利用GroundingDINO的無監督能力來追蹤網球運動員,而無需依賴標注數據,將復雜的目標檢測轉化為可操作的運動員追蹤。通過解決關鍵挑戰——例如區分運動員與其他場上人員、確??鐜囊恢伦粉櫼约皩⑦\動員運動映射到球場的鳥瞰圖——我們為無需顯式標簽的穩健追蹤管道奠定了基礎。

這種方法不僅解鎖了移動距離、速度和位置等洞察,還為更深入的比賽分析(如擊球目標和戰略覆蓋)打開了大門。通過進一步改進,包括從GroundingDINO輸出中提煉YOLO或RT-DETR模型,我們甚至可以開發出與現有商業解決方案相媲美的實時追蹤系統,為網球世界的教練和球迷參與提供強大的工具。

責任編輯:趙寧寧 來源: 小白玩轉Python
相關推薦

2021-08-24 10:35:44

體育運動物聯網IOT

2024-08-08 09:08:33

3DAI應用

2016-11-15 14:42:32

華為軟件開發云

2016-12-14 14:15:42

業務人員利用率

2022-07-01 19:12:59

戴爾

2020-09-24 10:49:50

機器人人工智能系統

2018-05-04 07:21:48

物聯網設備物聯網可穿戴設備

2020-01-10 17:30:57

信息安全漏洞技術

2022-09-14 12:59:27

人工智能運動課程足球比賽

2017-10-12 11:19:06

飛魚星

2021-07-20 18:59:00

裁判人工智能AI

2011-04-02 12:00:32

Aruba網絡

2011-02-23 12:28:11

Aruba澳大利亞網球協會

2021-09-27 15:13:00

小米MIUI12.5

2014-12-24 13:53:48

2019-07-25 14:45:19

2018-06-27 08:25:53

技術VAR人工智能

2021-07-21 08:37:55

AI 裁判人工智能
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 玖玖国产 | 亚洲高清在线免费观看 | 成人精品国产一区二区4080 | 久久天堂| 99re在线观看 | 色香婷婷 | av在线免费网 | 91久久伊人 | 毛片免费看 | 999久久久| 欧美一区免费 | 欧美自拍一区 | 日本精品久久久久久久 | 精品91久久 | 久久国内精品 | 久久久久久久久久影视 | 国产香蕉视频在线播放 | 天天干天天谢 | 亚洲欧美中文日韩在线 | 成年网站在线观看 | 国产精品日韩欧美 | 免费h视频 | 亚洲人人 | 精品欧美 | 欧美日韩亚洲成人 | 成人免费一区二区三区视频网站 | 欧美一级久久 | 亚洲第一中文字幕 | 国产精品国产精品 | 中文字幕在线播放第一页 | 99精品免费在线观看 | 欧美日韩在线观看视频 | 国产欧美精品一区二区色综合朱莉 | 久久久久久久久久久久91 | 国产精品久久久久久影视 | 日韩在线观看网站 | 精品一区二区三区电影 | 久草热8精品视频在线观看 午夜伦4480yy私人影院 | 亚洲永久入口 | 国产精久久久久久久妇剪断 | 亚洲高清网 |