betty0's picture
Deploy fall-detection-pose Gradio demo
11fab85 verified
Raw
History Blame Contribute Delete
2.89 kB
"""URFD 標註 CSV → ground-truth 跌倒事件區間。
URFD 的 ``urfall-cam0-falls.csv`` / ``urfall-cam0-adls.csv`` 無標頭,
欄位依序為:sequenceName, frameNumber, label, 之後為深度特徵欄(本專案不用)。
label 語意(官方):-1 = 未躺、0 = 跌落中(過渡姿態)、1 = 躺地。
GT 事件慣例(文獻對正類定義分歧,本專案明文寫死,README 同步聲明):
GT 事件區間 = [第一個 label=0 的幀, 緊接其後連續 label=1 區段的最後一幀]
理由:告警依設計在「躺地持續」後觸發;若 GT 只含 label=0 的短暫過渡段,
設計正確的告警反而會被判為 miss。
"""
from __future__ import annotations
from pathlib import Path
import pandas as pd
def load_annotations(csv_path: str | Path) -> pd.DataFrame:
"""讀取 URFD 標註 CSV,回傳欄位 ``sequence / frame / label`` 的 DataFrame。"""
df = pd.read_csv(csv_path, header=None)
df = df.iloc[:, :3]
df.columns = ["sequence", "frame", "label"]
df["frame"] = df["frame"].astype(int)
df["label"] = df["label"].astype(int)
return df
def gt_interval(labels: pd.DataFrame) -> tuple[int, int] | None:
"""單一序列的 GT 事件區間(幀號);無跌倒標記(純 ADL)回傳 None。
邊界處理:
- 若 0 之後找不到任何 1(理論上不會發生):以連續 0 區段末幀收尾;
- 若完全沒有 0 但有 1(不合規格的標註):以 1 區段起訖充當。
"""
seq = labels.sort_values("frame")
frames = seq["frame"].to_numpy()
lab = seq["label"].to_numpy()
zero_idx = [i for i, v in enumerate(lab) if v == 0]
if not zero_idx:
one_idx = [i for i, v in enumerate(lab) if v == 1]
if not one_idx:
return None
start = one_idx[0]
end = start
while end + 1 < len(lab) and lab[end + 1] == 1:
end += 1
return int(frames[start]), int(frames[end])
start = zero_idx[0]
# 找 0 之後第一個 1,再走到該連續 1 區段結束
i = start
first_one = None
for j in range(start, len(lab)):
if lab[j] == 1:
first_one = j
break
if first_one is None:
end = start
while end + 1 < len(lab) and lab[end + 1] == 0:
end += 1
return int(frames[start]), int(frames[end])
end = first_one
while end + 1 < len(lab) and lab[end + 1] == 1:
end += 1
return int(frames[start]), int(frames[end])
def load_gt_events(csv_path: str | Path) -> dict[str, tuple[int, int]]:
"""整份標註 CSV → {sequence 名: (起幀, 訖幀)};無事件的序列不在字典中。"""
df = load_annotations(csv_path)
out: dict[str, tuple[int, int]] = {}
for name, grp in df.groupby("sequence", sort=True):
interval = gt_interval(grp)
if interval is not None:
out[str(name)] = interval
return out