Fix: inline self-contained MediaPipe pose (drop futheros.adapters dependency)
Browse files- futheros/pipeline.py +38 -5
futheros/pipeline.py
CHANGED
|
@@ -271,11 +271,44 @@ def shooter_frame(holder, xy, goal, goal_f, n):
|
|
| 271 |
|
| 272 |
# ----------------------------------------------------------------------------- pose
|
| 273 |
_POSE = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
def _pose():
|
| 275 |
global _POSE
|
| 276 |
if _POSE is None:
|
| 277 |
-
|
| 278 |
-
_POSE = MediaPipePose()
|
| 279 |
return _POSE
|
| 280 |
|
| 281 |
|
|
@@ -289,10 +322,10 @@ def shooter_pose(clip, players, xy, kicker, kf, W, H):
|
|
| 289 |
cap = cv2.VideoCapture(clip); cap.set(cv2.CAP_PROP_POS_FRAMES, kf); ok, fr = cap.read(); cap.release()
|
| 290 |
if not ok or x1-x0 < 8 or y1-y0 < 8:
|
| 291 |
return None, (x0, y0, x1, y1), "?"
|
| 292 |
-
|
| 293 |
-
if
|
| 294 |
return None, (x0, y0, x1, y1), "?"
|
| 295 |
-
pts =
|
| 296 |
if not np.isnan(xy[kf, 0]):
|
| 297 |
bx, by = xy[kf]
|
| 298 |
foot = "L" if np.hypot(*(pts[L_ANKLE]-[bx, by])) < np.hypot(*(pts[R_ANKLE]-[bx, by])) else "R"
|
|
|
|
| 271 |
|
| 272 |
# ----------------------------------------------------------------------------- pose
|
| 273 |
_POSE = None
|
| 274 |
+
_POSE_URL = ("https://storage.googleapis.com/mediapipe-models/pose_landmarker/"
|
| 275 |
+
"pose_landmarker_full/float16/latest/pose_landmarker_full.task")
|
| 276 |
+
_POSE_FILE = "/tmp/pose_landmarker_full.task"
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
class _MPPose:
|
| 280 |
+
"""Self-contained MediaPipe Tasks PoseLandmarker (2D image-plane landmarks only)."""
|
| 281 |
+
|
| 282 |
+
def __init__(self):
|
| 283 |
+
import urllib.request
|
| 284 |
+
import mediapipe as mp
|
| 285 |
+
from mediapipe.tasks import python
|
| 286 |
+
from mediapipe.tasks.python import vision
|
| 287 |
+
self._mp = mp
|
| 288 |
+
if not os.path.exists(_POSE_FILE):
|
| 289 |
+
urllib.request.urlretrieve(_POSE_URL, _POSE_FILE)
|
| 290 |
+
opts = vision.PoseLandmarkerOptions(
|
| 291 |
+
base_options=python.BaseOptions(model_asset_path=_POSE_FILE),
|
| 292 |
+
running_mode=vision.RunningMode.IMAGE, num_poses=1,
|
| 293 |
+
min_pose_detection_confidence=0.5, min_pose_presence_confidence=0.5,
|
| 294 |
+
min_tracking_confidence=0.5)
|
| 295 |
+
self._lm = vision.PoseLandmarker.create_from_options(opts)
|
| 296 |
+
|
| 297 |
+
def xy(self, frame_bgr):
|
| 298 |
+
h, w = frame_bgr.shape[:2]
|
| 299 |
+
img = self._mp.Image(image_format=self._mp.ImageFormat.SRGB,
|
| 300 |
+
data=np.ascontiguousarray(frame_bgr[:, :, ::-1]))
|
| 301 |
+
res = self._lm.detect(img)
|
| 302 |
+
if not res.pose_landmarks:
|
| 303 |
+
return None
|
| 304 |
+
lms = res.pose_landmarks[0]
|
| 305 |
+
return np.array([[lm.x * w, lm.y * h] for lm in lms], dtype=np.float32)
|
| 306 |
+
|
| 307 |
+
|
| 308 |
def _pose():
|
| 309 |
global _POSE
|
| 310 |
if _POSE is None:
|
| 311 |
+
_POSE = _MPPose()
|
|
|
|
| 312 |
return _POSE
|
| 313 |
|
| 314 |
|
|
|
|
| 322 |
cap = cv2.VideoCapture(clip); cap.set(cv2.CAP_PROP_POS_FRAMES, kf); ok, fr = cap.read(); cap.release()
|
| 323 |
if not ok or x1-x0 < 8 or y1-y0 < 8:
|
| 324 |
return None, (x0, y0, x1, y1), "?"
|
| 325 |
+
fxy = _pose().xy(fr[y0:y1, x0:x1])
|
| 326 |
+
if fxy is None:
|
| 327 |
return None, (x0, y0, x1, y1), "?"
|
| 328 |
+
pts = fxy + np.array([x0, y0]); foot = "?"
|
| 329 |
if not np.isnan(xy[kf, 0]):
|
| 330 |
bx, by = xy[kf]
|
| 331 |
foot = "L" if np.hypot(*(pts[L_ANKLE]-[bx, by])) < np.hypot(*(pts[R_ANKLE]-[bx, by])) else "R"
|