Upload 2 files
Browse files- api.py +17 -2
- character_detection.py +12 -4
api.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
from fastapi import FastAPI, UploadFile, File, Form, BackgroundTasks, HTTPException
|
| 3 |
-
from fastapi.responses import JSONResponse
|
| 4 |
from fastapi.middleware.cors import CORSMiddleware
|
| 5 |
from pathlib import Path
|
| 6 |
import shutil
|
|
@@ -10,6 +10,7 @@ import uuid
|
|
| 10 |
from datetime import datetime
|
| 11 |
from typing import Dict
|
| 12 |
from enum import Enum
|
|
|
|
| 13 |
|
| 14 |
from video_processing import process_video_pipeline
|
| 15 |
from casting_loader import ensure_chroma, build_faces_index, build_voices_index
|
|
@@ -125,6 +126,19 @@ def get_job_status(job_id: str):
|
|
| 125 |
|
| 126 |
return response
|
| 127 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
def process_video_job(job_id: str):
|
| 129 |
"""
|
| 130 |
Procesa el vídeo de forma asíncrona.
|
|
@@ -155,7 +169,8 @@ def process_video_job(job_id: str):
|
|
| 155 |
video_path=video_path,
|
| 156 |
output_base=str(base),
|
| 157 |
epsilon=epsilon,
|
| 158 |
-
min_cluster_size=min_cluster_size
|
|
|
|
| 159 |
)
|
| 160 |
|
| 161 |
characters = result.get("characters", [])
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
from fastapi import FastAPI, UploadFile, File, Form, BackgroundTasks, HTTPException
|
| 3 |
+
from fastapi.responses import JSONResponse, FileResponse
|
| 4 |
from fastapi.middleware.cors import CORSMiddleware
|
| 5 |
from pathlib import Path
|
| 6 |
import shutil
|
|
|
|
| 10 |
from datetime import datetime
|
| 11 |
from typing import Dict
|
| 12 |
from enum import Enum
|
| 13 |
+
import os
|
| 14 |
|
| 15 |
from video_processing import process_video_pipeline
|
| 16 |
from casting_loader import ensure_chroma, build_faces_index, build_voices_index
|
|
|
|
| 126 |
|
| 127 |
return response
|
| 128 |
|
| 129 |
+
@app.get("/files/{video_name}/{char_id}/{filename}")
|
| 130 |
+
def serve_character_file(video_name: str, char_id: str, filename: str):
|
| 131 |
+
"""
|
| 132 |
+
Sirve archivos estáticos de personajes (imágenes).
|
| 133 |
+
Ejemplo: /files/dif_catala_1/char1/representative.jpg
|
| 134 |
+
"""
|
| 135 |
+
file_path = TEMP_ROOT / video_name / char_id / filename
|
| 136 |
+
|
| 137 |
+
if not file_path.exists():
|
| 138 |
+
raise HTTPException(status_code=404, detail="File not found")
|
| 139 |
+
|
| 140 |
+
return FileResponse(file_path)
|
| 141 |
+
|
| 142 |
def process_video_job(job_id: str):
|
| 143 |
"""
|
| 144 |
Procesa el vídeo de forma asíncrona.
|
|
|
|
| 169 |
video_path=video_path,
|
| 170 |
output_base=str(base),
|
| 171 |
epsilon=epsilon,
|
| 172 |
+
min_cluster_size=min_cluster_size,
|
| 173 |
+
video_name=video_name
|
| 174 |
)
|
| 175 |
|
| 176 |
characters = result.get("characters", [])
|
character_detection.py
CHANGED
|
@@ -34,15 +34,17 @@ class CharacterDetector:
|
|
| 34 |
Detector de personajes que integra el trabajo de Ana.
|
| 35 |
"""
|
| 36 |
|
| 37 |
-
def __init__(self, video_path: str, output_base: Path):
|
| 38 |
"""
|
| 39 |
Args:
|
| 40 |
video_path: Ruta al archivo de vídeo
|
| 41 |
output_base: Directorio base para guardar resultados (ej: /tmp/temp/video_name)
|
|
|
|
| 42 |
"""
|
| 43 |
self.video_path = video_path
|
| 44 |
self.output_base = Path(output_base)
|
| 45 |
self.output_base.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 46 |
|
| 47 |
# Crear subdirectorios
|
| 48 |
self.faces_dir = self.output_base / "faces"
|
|
@@ -227,10 +229,14 @@ class CharacterDetector:
|
|
| 227 |
shutil.copy(representative_src, representative_dst)
|
| 228 |
|
| 229 |
# Metadata del personaje
|
|
|
|
|
|
|
|
|
|
| 230 |
characters.append({
|
| 231 |
"id": char_id,
|
| 232 |
"name": f"Personatge {cluster_id + 1}",
|
| 233 |
-
"image_path": str(char_dir / "representative.jpg"),
|
|
|
|
| 234 |
"num_faces": len(face_indices),
|
| 235 |
"folder": str(char_dir)
|
| 236 |
})
|
|
@@ -298,7 +304,8 @@ class CharacterDetector:
|
|
| 298 |
|
| 299 |
# Función de conveniencia para usar en el API
|
| 300 |
def detect_characters_from_video(video_path: str, output_base: str,
|
| 301 |
-
epsilon: float = 0.5, min_cluster_size: int = 2
|
|
|
|
| 302 |
"""
|
| 303 |
Función de alto nivel para detectar personajes en un vídeo.
|
| 304 |
|
|
@@ -307,11 +314,12 @@ def detect_characters_from_video(video_path: str, output_base: str,
|
|
| 307 |
output_base: Directorio base para guardar resultados
|
| 308 |
epsilon: Parámetro epsilon para DBSCAN
|
| 309 |
min_cluster_size: Tamaño mínimo de cluster
|
|
|
|
| 310 |
|
| 311 |
Returns:
|
| 312 |
Dict con resultados: {"characters": [...], "analysis_path": "..."}
|
| 313 |
"""
|
| 314 |
-
detector = CharacterDetector(video_path, Path(output_base))
|
| 315 |
characters, analysis_path = detector.detect_characters(epsilon, min_cluster_size)
|
| 316 |
|
| 317 |
return {
|
|
|
|
| 34 |
Detector de personajes que integra el trabajo de Ana.
|
| 35 |
"""
|
| 36 |
|
| 37 |
+
def __init__(self, video_path: str, output_base: Path, video_name: str = None):
|
| 38 |
"""
|
| 39 |
Args:
|
| 40 |
video_path: Ruta al archivo de vídeo
|
| 41 |
output_base: Directorio base para guardar resultados (ej: /tmp/temp/video_name)
|
| 42 |
+
video_name: Nombre del vídeo (para construir URLs)
|
| 43 |
"""
|
| 44 |
self.video_path = video_path
|
| 45 |
self.output_base = Path(output_base)
|
| 46 |
self.output_base.mkdir(parents=True, exist_ok=True)
|
| 47 |
+
self.video_name = video_name or self.output_base.name
|
| 48 |
|
| 49 |
# Crear subdirectorios
|
| 50 |
self.faces_dir = self.output_base / "faces"
|
|
|
|
| 229 |
shutil.copy(representative_src, representative_dst)
|
| 230 |
|
| 231 |
# Metadata del personaje
|
| 232 |
+
# Construir URL relativa para la imagen
|
| 233 |
+
image_url = f"/files/{self.video_name}/{char_id}/representative.jpg"
|
| 234 |
+
|
| 235 |
characters.append({
|
| 236 |
"id": char_id,
|
| 237 |
"name": f"Personatge {cluster_id + 1}",
|
| 238 |
+
"image_path": str(char_dir / "representative.jpg"), # Ruta local
|
| 239 |
+
"image_url": image_url, # URL para el API
|
| 240 |
"num_faces": len(face_indices),
|
| 241 |
"folder": str(char_dir)
|
| 242 |
})
|
|
|
|
| 304 |
|
| 305 |
# Función de conveniencia para usar en el API
|
| 306 |
def detect_characters_from_video(video_path: str, output_base: str,
|
| 307 |
+
epsilon: float = 0.5, min_cluster_size: int = 2,
|
| 308 |
+
video_name: str = None) -> Dict[str, Any]:
|
| 309 |
"""
|
| 310 |
Función de alto nivel para detectar personajes en un vídeo.
|
| 311 |
|
|
|
|
| 314 |
output_base: Directorio base para guardar resultados
|
| 315 |
epsilon: Parámetro epsilon para DBSCAN
|
| 316 |
min_cluster_size: Tamaño mínimo de cluster
|
| 317 |
+
video_name: Nombre del vídeo (para construir URLs)
|
| 318 |
|
| 319 |
Returns:
|
| 320 |
Dict con resultados: {"characters": [...], "analysis_path": "..."}
|
| 321 |
"""
|
| 322 |
+
detector = CharacterDetector(video_path, Path(output_base), video_name=video_name)
|
| 323 |
characters, analysis_path = detector.detect_characters(epsilon, min_cluster_size)
|
| 324 |
|
| 325 |
return {
|