Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,11 +6,9 @@ Nouveau endpoint /extract-features-frames :
|
|
| 6 |
→ Identique au pipeline d'entraînement Kaggle
|
| 7 |
"""
|
| 8 |
|
| 9 |
-
from fastapi import FastAPI, UploadFile, File, HTTPException
|
| 10 |
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
from fastapi.responses import JSONResponse
|
| 12 |
-
from pydantic import BaseModel
|
| 13 |
-
from typing import List
|
| 14 |
import numpy as np
|
| 15 |
import cv2
|
| 16 |
import torch
|
|
@@ -62,11 +60,6 @@ app.add_middleware(
|
|
| 62 |
)
|
| 63 |
|
| 64 |
|
| 65 |
-
# ── Modèle de requête pour les frames ────────────────────────
|
| 66 |
-
class FramesRequest(BaseModel):
|
| 67 |
-
frames: List[str] # liste de 16 strings base64 JPEG (224×224)
|
| 68 |
-
|
| 69 |
-
|
| 70 |
# ── Helper : base64 → numpy (H, W, 3) uint8 ──────────────────
|
| 71 |
def b64_to_frame(b64str: str) -> np.ndarray:
|
| 72 |
img_bytes = base64.b64decode(b64str)
|
|
@@ -122,25 +115,29 @@ def health():
|
|
| 122 |
|
| 123 |
|
| 124 |
@app.post("/extract-features-frames")
|
| 125 |
-
async def extract_features_frames(
|
| 126 |
"""
|
| 127 |
-
Reçoit : {
|
| 128 |
Retourne: { "features": [[...], ...], "shape": [8, 768] }
|
| 129 |
-
|
| 130 |
-
Les frames doivent être croppées sur la bouche côté browser
|
| 131 |
-
(MediaPipe FaceMesh) — exactement comme le dataset d'entraînement.
|
| 132 |
"""
|
| 133 |
if vmae_model is None:
|
| 134 |
raise HTTPException(status_code=503, detail="VideoMAE non chargé")
|
| 135 |
|
| 136 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
if n == 0:
|
| 138 |
raise HTTPException(status_code=422, detail="Aucune frame reçue")
|
| 139 |
|
| 140 |
-
# Padding ou troncature à
|
| 141 |
-
frames_b64 =
|
| 142 |
while len(frames_b64) < NUM_FRAMES:
|
| 143 |
-
frames_b64.append(frames_b64[-1])
|
| 144 |
|
| 145 |
try:
|
| 146 |
frames_np = [b64_to_frame(f) for f in frames_b64]
|
|
@@ -155,7 +152,7 @@ async def extract_features_frames(req: FramesRequest):
|
|
| 155 |
|
| 156 |
return JSONResponse({
|
| 157 |
"features": features.tolist(),
|
| 158 |
-
"shape": list(features.shape),
|
| 159 |
"model_id": VMAE_MODEL_ID,
|
| 160 |
"frames_received": n,
|
| 161 |
})
|
|
|
|
| 6 |
→ Identique au pipeline d'entraînement Kaggle
|
| 7 |
"""
|
| 8 |
|
| 9 |
+
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
|
| 10 |
from fastapi.middleware.cors import CORSMiddleware
|
| 11 |
from fastapi.responses import JSONResponse
|
|
|
|
|
|
|
| 12 |
import numpy as np
|
| 13 |
import cv2
|
| 14 |
import torch
|
|
|
|
| 60 |
)
|
| 61 |
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
# ── Helper : base64 → numpy (H, W, 3) uint8 ──────────────────
|
| 64 |
def b64_to_frame(b64str: str) -> np.ndarray:
|
| 65 |
img_bytes = base64.b64decode(b64str)
|
|
|
|
| 115 |
|
| 116 |
|
| 117 |
@app.post("/extract-features-frames")
|
| 118 |
+
async def extract_features_frames(frames_json: str = Form(...)):
|
| 119 |
"""
|
| 120 |
+
Reçoit : FormData { frames_json: "['<base64>', ...]" }
|
| 121 |
Retourne: { "features": [[...], ...], "shape": [8, 768] }
|
| 122 |
+
Utilise FormData (multipart) pour éviter le preflight CORS de HuggingFace.
|
|
|
|
|
|
|
| 123 |
"""
|
| 124 |
if vmae_model is None:
|
| 125 |
raise HTTPException(status_code=503, detail="VideoMAE non chargé")
|
| 126 |
|
| 127 |
+
try:
|
| 128 |
+
import json as _json
|
| 129 |
+
frames_list = _json.loads(frames_json)
|
| 130 |
+
except Exception:
|
| 131 |
+
raise HTTPException(status_code=422, detail="frames_json invalide")
|
| 132 |
+
|
| 133 |
+
n = len(frames_list)
|
| 134 |
if n == 0:
|
| 135 |
raise HTTPException(status_code=422, detail="Aucune frame reçue")
|
| 136 |
|
| 137 |
+
# Padding ou troncature à NUM_FRAMES
|
| 138 |
+
frames_b64 = frames_list[:NUM_FRAMES]
|
| 139 |
while len(frames_b64) < NUM_FRAMES:
|
| 140 |
+
frames_b64.append(frames_b64[-1])
|
| 141 |
|
| 142 |
try:
|
| 143 |
frames_np = [b64_to_frame(f) for f in frames_b64]
|
|
|
|
| 152 |
|
| 153 |
return JSONResponse({
|
| 154 |
"features": features.tolist(),
|
| 155 |
+
"shape": list(features.shape),
|
| 156 |
"model_id": VMAE_MODEL_ID,
|
| 157 |
"frames_received": n,
|
| 158 |
})
|