自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

基于 Kalman 濾波的無(wú)標(biāo)簽網(wǎng)球運(yùn)動(dòng)員追蹤

開(kāi)發(fā)
本項(xiàng)目展示了如何利用GroundingDINO的無(wú)監(jiān)督能力來(lái)追蹤網(wǎng)球運(yùn)動(dòng)員,而無(wú)需依賴標(biāo)注數(shù)據(jù),將復(fù)雜的目標(biāo)檢測(cè)轉(zhuǎn)化為可操作的運(yùn)動(dòng)員追蹤。

近年來(lái),隨著體育追蹤項(xiàng)目的興起,越來(lái)越多的體育愛(ài)好者開(kāi)始使用自動(dòng)化運(yùn)動(dòng)員追蹤技術(shù)。大多數(shù)方法遵循一個(gè)常見(jiàn)的工作流程:收集標(biāo)注數(shù)據(jù),訓(xùn)練YOLO模型,將運(yùn)動(dòng)員坐標(biāo)投影到場(chǎng)地或球場(chǎng)的俯視圖中,并利用這些追蹤數(shù)據(jù)生成高級(jí)分析,以獲取潛在的競(jìng)技洞察。然而,在本項(xiàng)目中,我們提供了一種工具,可以繞過(guò)對(duì)標(biāo)注數(shù)據(jù)的需求,轉(zhuǎn)而依賴GroundingDINO的無(wú)監(jiān)督追蹤能力,并結(jié)合Kalman濾波來(lái)克服GroundingDINO輸出的噪聲問(wèn)題。

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

對(duì)于不熟悉GroundingDINO的人來(lái)說(shuō),它將目標(biāo)檢測(cè)與語(yǔ)言相結(jié)合,允許用戶輸入提示,例如“一個(gè)網(wǎng)球運(yùn)動(dòng)員”,模型隨后會(huì)返回符合描述的候選目標(biāo)檢測(cè)框。RoboFlow提供了一個(gè)很好的教程,供有興趣使用的人參考——但我在下面也粘貼了一些非?;A(chǔ)的代碼。如下所示,你可以通過(guò)提示讓模型識(shí)別一些在目標(biāo)檢測(cè)數(shù)據(jù)集中很少被標(biāo)記的對(duì)象,比如狗狗和狗的舌頭。

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
)

然而,在職業(yè)網(wǎng)球場(chǎng)上區(qū)分運(yùn)動(dòng)員并不像提示“網(wǎng)球運(yùn)動(dòng)員”那么簡(jiǎn)單。模型經(jīng)常會(huì)錯(cuò)誤識(shí)別場(chǎng)上的其他人員,例如線審、球童和其他裁判,導(dǎo)致標(biāo)注跳躍且不一致。此外,模型有時(shí)甚至無(wú)法在某些幀中檢測(cè)到運(yùn)動(dòng)員,導(dǎo)致標(biāo)注框出現(xiàn)空白或無(wú)法持續(xù)出現(xiàn)在每一幀中。

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

為了解決這些挑戰(zhàn),我們應(yīng)用了幾種有針對(duì)性的方法。首先,我們將檢測(cè)框縮小到所有可能框中的前三個(gè)概率最高的框。通常,線審的概率得分高于運(yùn)動(dòng)員,這就是為什么我們不只過(guò)濾到兩個(gè)框。然而,這引發(fā)了一個(gè)新問(wèn)題:如何在每一幀中自動(dòng)區(qū)分運(yùn)動(dòng)員和線審?

我們觀察到,線和球工作人員的檢測(cè)框通常持續(xù)時(shí)間較短,往往只持續(xù)幾幀?;诖耍覀兗僭O(shè)通過(guò)關(guān)聯(lián)連續(xù)幀中的框,可以過(guò)濾掉那些只短暫出現(xiàn)的人員,從而隔離出運(yùn)動(dòng)員。

那么,我們?nèi)绾螌?shí)現(xiàn)跨幀對(duì)象之間的這種關(guān)聯(lián)呢?幸運(yùn)的是,多目標(biāo)追蹤領(lǐng)域已經(jīng)對(duì)這個(gè)問(wèn)題進(jìn)行了廣泛研究。Kalman濾波器是多目標(biāo)追蹤中的主要工具,通常與其他識(shí)別指標(biāo)(如顏色)結(jié)合使用。對(duì)于我們的目的,一個(gè)基本的Kalman濾波器實(shí)現(xiàn)就足夠了。簡(jiǎn)單來(lái)說(shuō)(更深入的探討可以參考這篇文章),Kalman濾波器是一種基于先前測(cè)量結(jié)果概率估計(jì)對(duì)象位置的方法。它在處理噪聲數(shù)據(jù)時(shí)特別有效,但也適用于在視頻中跨時(shí)間關(guān)聯(lián)對(duì)象,即使檢測(cè)不一致(例如運(yùn)動(dòng)員未被每一幀追蹤到)。我們?cè)谶@里實(shí)現(xiàn)了一個(gè)完整的Kalman濾波器,但將在接下來(lái)的段落中介紹一些主要步驟。

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

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

Kalman濾波器分為兩個(gè)步驟:首先預(yù)測(cè)對(duì)象在下一幀中的位置,然后根據(jù)新的測(cè)量結(jié)果(在我們的案例中來(lái)自目標(biāo)檢測(cè)器)更新預(yù)測(cè)。然而,在我們的示例中,新幀可能會(huì)有多個(gè)新對(duì)象,甚至可能會(huì)丟失前一幀中存在的對(duì)象,這就引出了如何將之前看到的框與當(dāng)前看到的框關(guān)聯(lián)起來(lái)的問(wèn)題。

我們選擇使用馬氏距離(Mahalanobis distance)結(jié)合卡方檢驗(yàn)來(lái)評(píng)估當(dāng)前檢測(cè)與過(guò)去對(duì)象匹配的概率。此外,我們保留了一個(gè)過(guò)去對(duì)象的隊(duì)列,以便擁有比一幀更長(zhǎng)的“記憶”。具體來(lái)說(shuō),我們的記憶存儲(chǔ)了過(guò)去30幀中看到的任何對(duì)象的軌跡。然后,對(duì)于我們?cè)谛聨姓业降拿總€(gè)對(duì)象,我們遍歷我們的記憶,找到最可能與當(dāng)前對(duì)象匹配的先前對(duì)象,匹配概率由馬氏距離給出。然而,我們也可能看到一個(gè)全新的對(duì)象,在這種情況下,我們應(yīng)該將一個(gè)新對(duì)象添加到我們的記憶中。如果任何對(duì)象與記憶中的任何框的關(guān)聯(lián)概率小于30%,我們將其作為新對(duì)象添加到記憶中。

完整的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)

在追蹤了過(guò)去30幀中檢測(cè)到的每個(gè)對(duì)象后,我們現(xiàn)在可以設(shè)計(jì)啟發(fā)式方法來(lái)精確定位哪些框最有可能代表我們的運(yùn)動(dòng)員。我們測(cè)試了兩種方法:選擇最靠近底線中心的框,以及選擇在我們記憶中觀察歷史最長(zhǎng)的框。經(jīng)驗(yàn)上,第一種策略在實(shí)際運(yùn)動(dòng)員遠(yuǎn)離底線時(shí)經(jīng)常將線審標(biāo)記為運(yùn)動(dòng)員,使其不太可靠。與此同時(shí),我們注意到GroundingDino往往在不同的線審和球童之間“閃爍”,而真正的運(yùn)動(dòng)員則保持相對(duì)穩(wěn)定的存在。因此,我們的最終規(guī)則是選擇記憶中追蹤歷史最長(zhǎng)的框作為真正的運(yùn)動(dòng)員。正如你在初始視頻中看到的,對(duì)于如此簡(jiǎn)單的規(guī)則來(lái)說(shuō),它的效果出奇地好!

現(xiàn)在,我們的追蹤系統(tǒng)已經(jīng)在圖像上建立,我們可以轉(zhuǎn)向更傳統(tǒng)的分析,從鳥(niǎo)瞰視角追蹤運(yùn)動(dòng)員。這種視角可以評(píng)估關(guān)鍵指標(biāo),例如總移動(dòng)距離、運(yùn)動(dòng)員速度和球場(chǎng)位置趨勢(shì)。例如,我們可以分析運(yùn)動(dòng)員是否經(jīng)常根據(jù)比賽中的位置針對(duì)對(duì)手的反手。為了實(shí)現(xiàn)這一點(diǎn),我們需要將運(yùn)動(dòng)員坐標(biāo)從圖像投影到標(biāo)準(zhǔn)化的球場(chǎng)模板上,從上方對(duì)齊視角以進(jìn)行空間分析。

這就是單應(yīng)性變換(homography)發(fā)揮作用的地方。單應(yīng)性描述了兩個(gè)表面之間的映射關(guān)系,在我們的案例中,這意味著將原始圖像中的點(diǎn)映射到俯視的球場(chǎng)視圖。通過(guò)在原始圖像中識(shí)別一些關(guān)鍵點(diǎn)(例如球場(chǎng)上的線交叉點(diǎn)),我們可以計(jì)算一個(gè)單應(yīng)性矩陣,將任何點(diǎn)轉(zhuǎn)換為鳥(niǎo)瞰圖。為了創(chuàng)建這個(gè)單應(yīng)性矩陣,我們首先需要識(shí)別這些“關(guān)鍵點(diǎn)”。RoboFlow等平臺(tái)上的各種開(kāi)源、許可寬松的模型可以幫助檢測(cè)這些點(diǎn),或者我們可以在參考圖像上手動(dòng)標(biāo)記它們以用于變換。

正如你所看到的,預(yù)測(cè)的關(guān)鍵點(diǎn)并不完美,但我們發(fā)現(xiàn)小的誤差對(duì)最終的變換矩陣影響不大

在標(biāo)記這些關(guān)鍵點(diǎn)后,下一步是將它們與參考球場(chǎng)圖像上的對(duì)應(yīng)點(diǎn)匹配,以生成單應(yīng)性矩陣。使用OpenCV,我們可以用幾行簡(jiǎn)單的代碼創(chuàng)建這個(gè)變換矩陣:

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)

有了單應(yīng)性矩陣,我們可以將圖像中的任何點(diǎn)映射到參考球場(chǎng)上。對(duì)于這個(gè)項(xiàng)目,我們的重點(diǎn)是運(yùn)動(dòng)員在球場(chǎng)上的位置。為了確定這一點(diǎn),我們?nèi)∶總€(gè)運(yùn)動(dòng)員邊界框底部的中心點(diǎn),將其作為他們?cè)邙B(niǎo)瞰圖中的球場(chǎng)位置。

我們使用框底部的中心點(diǎn)來(lái)映射每個(gè)運(yùn)動(dòng)員在球場(chǎng)上的位置。圖示顯示了通過(guò)我們的單應(yīng)性矩陣將關(guān)鍵點(diǎn)轉(zhuǎn)換為鳥(niǎo)瞰圖中的網(wǎng)球球場(chǎng)

總之,本項(xiàng)目展示了如何利用GroundingDINO的無(wú)監(jiān)督能力來(lái)追蹤網(wǎng)球運(yùn)動(dòng)員,而無(wú)需依賴標(biāo)注數(shù)據(jù),將復(fù)雜的目標(biāo)檢測(cè)轉(zhuǎn)化為可操作的運(yùn)動(dòng)員追蹤。通過(guò)解決關(guān)鍵挑戰(zhàn)——例如區(qū)分運(yùn)動(dòng)員與其他場(chǎng)上人員、確??鐜囊恢伦粉櫼约皩⑦\(yùn)動(dòng)員運(yùn)動(dòng)映射到球場(chǎng)的鳥(niǎo)瞰圖——我們?yōu)闊o(wú)需顯式標(biāo)簽的穩(wěn)健追蹤管道奠定了基礎(chǔ)。

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

責(zé)任編輯:趙寧寧 來(lái)源: 小白玩轉(zhuǎn)Python
相關(guān)推薦

2021-08-24 10:35:44

體育運(yùn)動(dòng)物聯(lián)網(wǎng)IOT

2024-08-08 09:08:33

3DAI應(yīng)用

2016-11-15 14:42:32

華為軟件開(kāi)發(fā)云

2016-12-14 14:15:42

業(yè)務(wù)人員利用率

2022-07-01 19:12:59

戴爾

2020-09-24 10:49:50

機(jī)器人人工智能系統(tǒng)

2018-05-04 07:21:48

物聯(lián)網(wǎng)設(shè)備物聯(lián)網(wǎng)可穿戴設(shè)備

2021-07-20 18:59:00

裁判人工智能AI

2020-01-10 17:30:57

信息安全漏洞技術(shù)

2017-10-12 11:19:06

飛魚(yú)星

2022-09-14 12:59:27

人工智能運(yùn)動(dòng)課程足球比賽

2021-09-27 15:13:00

小米MIUI12.5

2011-04-02 12:00:32

Aruba網(wǎng)絡(luò)

2011-02-23 12:28:11

Aruba澳大利亞網(wǎng)球協(xié)會(huì)

2014-12-24 13:53:48

2021-07-21 08:37:55

AI 裁判人工智能

2024-08-19 07:40:00

奧運(yùn)計(jì)算機(jī)

2015-07-17 10:02:48

寫(xiě)代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)