VSL_Recognition / models /preprocess.py
BaMiaKhoGa's picture
feat: signer-independent split + spatial augmentation (74.66% honest) + sample videos
7cb2766
Raw History Blame Contribute Delete
2.25 kB
"""
Chuẩn hoá theo người ký (signer-relative normalization) — làm keypoint bất biến
với khung hình / khoảng cách / vị trí trong khung.
Dùng CHUNG cho cả huấn luyện (scripts/train_clean.py --signer) lẫn suy luận
(app/inference/pipeline.py). Chỉ có MỘT bản duy nhất để train và inference không
bao giờ lệch nhau.
Ý tưởng: căn mọi điểm về GIỮA HAI VAI và chia cho ĐỘ RỘNG VAI. Sau phép này,
người đứng gần/xa, lệch trái/phải, khung rộng/hẹp đều cho cùng toạ độ — model chỉ
còn thấy hình dạng + chuyển động của tay so với cơ thể.
Bố cục 75 keypoint: bàn tay trái 0–20, tay phải 21–41, pose 42–74.
Vai trong pose: landmark 11 (trái) → 42+11=53, landmark 12 (phải) → 42+12=54.
"""
from __future__ import annotations
import numpy as np
_LS = 53 # vai trái (pose landmark 11)
_RS = 54 # vai phải (pose landmark 12)
def signer_normalize(kp: np.ndarray) -> np.ndarray:
"""
kp: (T, 75, 3) — toạ độ thô (đã chuẩn hoá theo khung hình, điểm thiếu = 0).
Trả về (T, 75, 3) đã căn giữa vai + chia độ rộng vai. Điểm thiếu giữ nguyên 0.
Nếu không đủ dữ liệu vai để ước lượng (cả clip không thấy vai), trả về nguyên
bản — thà không chuẩn hoá còn hơn chuẩn hoá sai.
"""
kp = np.asarray(kp, dtype=np.float32)
ls = kp[:, _LS, :] # (T, 3)
rs = kp[:, _RS, :] # (T, 3)
detected = (np.any(ls != 0.0, axis=-1)) & (np.any(rs != 0.0, axis=-1)) # (T,)
if detected.sum() == 0:
return kp.copy()
# Tâm = trung điểm hai vai, lấy trung bình qua các frame thấy đủ vai
mid = ((ls[detected] + rs[detected]) / 2.0).mean(axis=0) # (3,)
# Thang = độ rộng vai (khoảng cách x-y), trung bình qua các frame đó
width = np.linalg.norm((ls[detected] - rs[detected])[:, :2], axis=1).mean()
if width < 1e-6:
return kp.copy()
mask = np.any(kp != 0.0, axis=-1, keepdims=True) # (T, 75, 1) — điểm đã phát hiện
normed = (kp - mid) / width
return np.where(mask, normed, 0.0).astype(np.float32)