用 SAM3 + Homography 追蹤 NBA 球員:把直播鏡頭變成即時俯視軌跡圖
把 Meta 的影片分割模型接上透視投影,讓每個球員的移動路徑自動投影到俯視球場圖上。
前言
看 NBA 直播的時候,鏡頭永遠跟著球跑,球員散落在整個半場各個角落。教練要分析戰術,往往得一格一格回放,還要手動標記位置,耗時又費力。
如果能讓 AI 自動追蹤每個球員、即時畫出他們在球場上的移動軌跡,這件事就有趣多了。
這篇文章記錄的是一個週末實驗:用 Meta 的 SAM3(SAM2.1)對 NBA 影片做球員分割,再透過 Homography 透視投影,把每個球員的腳底位置換算成俯視球場座標。最終輸出兩支影片——一支是疊加彩色 mask 的原始視角,另一支是即時更新軌跡的俯視鳥瞰圖。

SAM3 是什麼?跟一般 tracking 有什麼不同?
SAM(Segment Anything Model)是 Meta AI 推出的通用分割模型,SAM2 / SAM3 進一步支援影片序列:在第一幀給一個點或框,後續幀就會自動傳播分割結果。
和傳統做法相比,差異很明顯:
- YOLO + ByteTrack:偵測邊框(bounding box)→ 指派 ID → 逐幀追蹤。快,但只有矩形框,沒有像素級輪廓。
- SAM3:給一個點,模型輸出整個人的像素 mask,後續幀持續追蹤這個物件。結果是彩色的人形遮罩,不是框框。
對這個實驗來說,mask 的好處是可以精確算出「腳底位置」——取 mask 最底部的像素中心,投影到俯視圖的準確度,比框框底邊中心高出不少。
整體架構:兩個技術各司其職
整個系統的核心只有兩件事:
SAM3 負責「誰在哪裡」——輸出每個球員在每一幀的像素 mask。
Homography 負責「換算座標」——把攝影機視角的像素座標,透過透視矩陣換算成標準俯視球場座標。
兩件事串起來,就能在俯視圖上即時畫出軌跡。
實作流程
第一步:在第一幀點選球員
程式啟動後會彈出第一幀的視窗,用滑鼠點在想追蹤的球員身上,每點一下就加入一個追蹤對象:
def on_mouse(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
player_points.append((x, y))
cv2.circle(display_frame, (x, y), 8, COLORS[len(player_points)-1], -1)
點完後按 Enter,這些點就成為 SAM3 在第 0 幀的初始 prompt。
第二步:SAM3 傳播 mask
SAM3 的 video predictor 需要先把影片匯出成圖片序列,再初始化 inference state:
predictor = build_sam2_video_predictor(MODEL_CFG, CHECKPOINT, device=device)
inference_state = predictor.init_state(video_path=FRAMES_DIR)
# 對每個球員加入初始點
for obj_id, (px, py) in enumerate(player_points):
predictor.add_new_points_or_box(
inference_state=inference_state,
frame_idx=0,
obj_id=obj_id,
points=np.array([[px, py]], dtype=np.float32),
labels=np.array([1], dtype=np.int32),
)
接著呼叫 propagate_in_video,模型就會逐幀回傳每個物件的 mask logits。
第三步:投影到俯視球場
這是整個流程最需要手動調整的部分。先在影片第一幀上標出球場四個角點的像素座標,再用 cv2.findHomography 算出透視矩陣:
COURT_SRC = np.float32([
[120, 680], # 左下角
[1160, 680], # 右下角
[980, 120], # 右上角
[300, 120], # 左上角
])
H_matrix, _ = cv2.findHomography(COURT_SRC, COURT_DST)
每一幀取出球員 mask 的腳底位置後,用這個矩陣換算:
rows = np.where(mask.any(axis=1))[0]
cols = np.where(mask.any(axis=0))[0]
foot_y = rows[-1] # mask 最底部
foot_x = int(cols.mean()) # 水平中心
bird_pt = cv2.perspectiveTransform(
np.float32([[foot_x, foot_y]]).reshape(-1, 1, 2),
H_matrix
)[0][0].astype(int)
投影結果直接畫到俯視球場底圖上,並記錄軌跡點,越舊的軌跡顏色越淡,視覺上就能清楚看出移動方向與速度。
效果與已知限制
最終輸出兩支影片:tracked_video.mp4(原視角疊加彩色 mask)和 birdview_video.mp4(俯視軌跡動畫)。
實際跑起來效果不錯,mask 在球員移動時追蹤穩定,俯視軌跡也能清楚呈現球員的跑位路線。
不過有幾個已知的坑要注意:
- 沒有 GPU 非常慢:CPU 模式下一幀可能要 2–5 秒,建議丟到 Colab 掛 T4 跑。
- 球員遮擋:兩個球員重疊時,mask 容易漂移或合併,需要在中途補加 point hint 重新鎖定。
- 角點標定要手動:不同鏡頭角度、不同場館,
COURT_SRC四個角點都要重新標,目前沒有自動化這一步。
結語
這個實驗把 SAM3 的影片分割能力和 Homography 的座標轉換串在一起,用相對少量的程式碼,就做出了一個可以看的球員追蹤系統。
後續能延伸的方向很多:累積多場比賽的軌跡來生成熱力圖、識別球隊陣型、或接上 YOLO 做第一幀的自動初始化,省掉手動點選的步驟。如果你對 CV 或球賽分析有興趣,這是個不錯的起點。