Spaces:
Sleeping
Sleeping
fixig some things
Browse files
server.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, status, Depends, UploadFile, File, Form
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from fastapi.responses import StreamingResponse
|
| 4 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
@@ -14,6 +14,7 @@ import threading
|
|
| 14 |
import os
|
| 15 |
import base64
|
| 16 |
import hashlib
|
|
|
|
| 17 |
from pydantic import BaseModel, Field
|
| 18 |
from pymongo import AsyncMongoClient
|
| 19 |
from passlib.context import CryptContext
|
|
@@ -51,6 +52,10 @@ current_cx = 0.5
|
|
| 51 |
current_cy = 0.5
|
| 52 |
current_scale = 1.0
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
# Configurable parameters for smooth panning
|
| 55 |
SMOOTHING_FACTOR = 0.1 # Lower is smoother but slower (similar to Dart's TweenAnimation)
|
| 56 |
TARGET_ASPECT_RATIO = 16.0 / 9.0 # Assuming output is meant to be 16:9
|
|
@@ -204,7 +209,7 @@ def apply_center_stage_crop(frame, tracking_data):
|
|
| 204 |
the frame based on the tracking target bounding box.
|
| 205 |
Returns the cropped frame.
|
| 206 |
"""
|
| 207 |
-
global current_cx, current_cy, current_scale
|
| 208 |
|
| 209 |
h, w = frame.shape[:2]
|
| 210 |
|
|
@@ -213,6 +218,8 @@ def apply_center_stage_crop(frame, tracking_data):
|
|
| 213 |
target_cy = 0.5
|
| 214 |
target_scale = 1.0
|
| 215 |
|
|
|
|
|
|
|
| 216 |
# Calculate target state based on tracking data
|
| 217 |
boxes = tracking_data.get("boxes", [])
|
| 218 |
if tracking_data.get("mode") == "multi":
|
|
@@ -225,6 +232,7 @@ def apply_center_stage_crop(frame, tracking_data):
|
|
| 225 |
|
| 226 |
target_cx = box_cx / w
|
| 227 |
target_cy = box_cy / h
|
|
|
|
| 228 |
|
| 229 |
# Target scale logic (from Dart): max dimension proportion * 1.5 margin
|
| 230 |
max_dim = max(box_w / w, box_h / h)
|
|
@@ -246,11 +254,27 @@ def apply_center_stage_crop(frame, tracking_data):
|
|
| 246 |
|
| 247 |
target_cx = box_cx / w
|
| 248 |
target_cy = box_cy / h
|
|
|
|
| 249 |
|
| 250 |
max_dim = max(box_w / w, box_h / h)
|
| 251 |
target_scale = 1.0 / (max_dim * 2.0) # slightly tighter for single person
|
| 252 |
target_scale = max(1.0, min(target_scale, 3.0))
|
| 253 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
# Apply EMA smoothing
|
| 255 |
current_cx += (target_cx - current_cx) * SMOOTHING_FACTOR
|
| 256 |
current_cy += (target_cy - current_cy) * SMOOTHING_FACTOR
|
|
@@ -897,6 +921,45 @@ async def set_desired_angle(
|
|
| 897 |
logger.error(f"Error setting angle in DB: {e}")
|
| 898 |
raise HTTPException(status_code=500, detail=str(e))
|
| 899 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 900 |
@app.get("/api/audio/settings")
|
| 901 |
async def get_audio_settings():
|
| 902 |
"""Retrieve all audio settings from MongoDB."""
|
|
|
|
| 1 |
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, HTTPException, status, Depends, UploadFile, File, Form, Body
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from fastapi.responses import StreamingResponse
|
| 4 |
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
|
|
| 14 |
import os
|
| 15 |
import base64
|
| 16 |
import hashlib
|
| 17 |
+
import math
|
| 18 |
from pydantic import BaseModel, Field
|
| 19 |
from pymongo import AsyncMongoClient
|
| 20 |
from passlib.context import CryptContext
|
|
|
|
| 52 |
current_cy = 0.5
|
| 53 |
current_scale = 1.0
|
| 54 |
|
| 55 |
+
# --- Real-time Target Tracking State ---
|
| 56 |
+
current_target_angle = None
|
| 57 |
+
current_target_distance = None
|
| 58 |
+
|
| 59 |
# Configurable parameters for smooth panning
|
| 60 |
SMOOTHING_FACTOR = 0.1 # Lower is smoother but slower (similar to Dart's TweenAnimation)
|
| 61 |
TARGET_ASPECT_RATIO = 16.0 / 9.0 # Assuming output is meant to be 16:9
|
|
|
|
| 209 |
the frame based on the tracking target bounding box.
|
| 210 |
Returns the cropped frame.
|
| 211 |
"""
|
| 212 |
+
global current_cx, current_cy, current_scale, current_target_angle, current_target_distance
|
| 213 |
|
| 214 |
h, w = frame.shape[:2]
|
| 215 |
|
|
|
|
| 218 |
target_cy = 0.5
|
| 219 |
target_scale = 1.0
|
| 220 |
|
| 221 |
+
target_found = False
|
| 222 |
+
|
| 223 |
# Calculate target state based on tracking data
|
| 224 |
boxes = tracking_data.get("boxes", [])
|
| 225 |
if tracking_data.get("mode") == "multi":
|
|
|
|
| 232 |
|
| 233 |
target_cx = box_cx / w
|
| 234 |
target_cy = box_cy / h
|
| 235 |
+
target_found = True
|
| 236 |
|
| 237 |
# Target scale logic (from Dart): max dimension proportion * 1.5 margin
|
| 238 |
max_dim = max(box_w / w, box_h / h)
|
|
|
|
| 254 |
|
| 255 |
target_cx = box_cx / w
|
| 256 |
target_cy = box_cy / h
|
| 257 |
+
target_found = True
|
| 258 |
|
| 259 |
max_dim = max(box_w / w, box_h / h)
|
| 260 |
target_scale = 1.0 / (max_dim * 2.0) # slightly tighter for single person
|
| 261 |
target_scale = max(1.0, min(target_scale, 3.0))
|
| 262 |
|
| 263 |
+
if target_found:
|
| 264 |
+
# Calculate distance and angle from the frame center (w/2, h/2) to the target bounding box center (box_cx, box_cy)
|
| 265 |
+
center_x, center_y = w / 2.0, h / 2.0
|
| 266 |
+
|
| 267 |
+
dx = box_cx - center_x
|
| 268 |
+
dy = box_cy - center_y
|
| 269 |
+
|
| 270 |
+
current_target_distance = math.hypot(dx, dy)
|
| 271 |
+
# Convert atan2 result to 0-360 degrees
|
| 272 |
+
angle = math.degrees(math.atan2(dy, dx))
|
| 273 |
+
current_target_angle = angle % 360.0
|
| 274 |
+
else:
|
| 275 |
+
current_target_angle = None
|
| 276 |
+
current_target_distance = None
|
| 277 |
+
|
| 278 |
# Apply EMA smoothing
|
| 279 |
current_cx += (target_cx - current_cx) * SMOOTHING_FACTOR
|
| 280 |
current_cy += (target_cy - current_cy) * SMOOTHING_FACTOR
|
|
|
|
| 921 |
logger.error(f"Error setting angle in DB: {e}")
|
| 922 |
raise HTTPException(status_code=500, detail=str(e))
|
| 923 |
|
| 924 |
+
@app.get("/api/audio/get-angle")
|
| 925 |
+
async def get_current_angle():
|
| 926 |
+
"""
|
| 927 |
+
Get the currently tracked angle of the target person.
|
| 928 |
+
If no person is tracked, fallback to the angle previously set via set-angle.
|
| 929 |
+
"""
|
| 930 |
+
try:
|
| 931 |
+
global current_target_angle, current_target_distance
|
| 932 |
+
|
| 933 |
+
# If a person is actively being tracked, return their real-time angle
|
| 934 |
+
if current_target_angle is not None:
|
| 935 |
+
return {
|
| 936 |
+
"ok": True,
|
| 937 |
+
"source": "tracking",
|
| 938 |
+
"angle": round(current_target_angle, 2),
|
| 939 |
+
"distance": round(current_target_distance, 2)
|
| 940 |
+
}
|
| 941 |
+
|
| 942 |
+
# Fallback to the saved angle if no target is actively tracked
|
| 943 |
+
if audio_angles_collection is not None:
|
| 944 |
+
saved_angle_doc = await audio_angles_collection.find_one({"key": "latest_angle"})
|
| 945 |
+
if saved_angle_doc and "value" in saved_angle_doc:
|
| 946 |
+
return {
|
| 947 |
+
"ok": True,
|
| 948 |
+
"source": "database",
|
| 949 |
+
"angle": float(saved_angle_doc["value"]),
|
| 950 |
+
"distance": None
|
| 951 |
+
}
|
| 952 |
+
|
| 953 |
+
return {
|
| 954 |
+
"ok": False,
|
| 955 |
+
"message": "No active tracking and no saved angle found",
|
| 956 |
+
"angle": None,
|
| 957 |
+
"distance": None
|
| 958 |
+
}
|
| 959 |
+
except Exception as e:
|
| 960 |
+
logger.error(f"Error retrieving angle: {e}")
|
| 961 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 962 |
+
|
| 963 |
@app.get("/api/audio/settings")
|
| 964 |
async def get_audio_settings():
|
| 965 |
"""Retrieve all audio settings from MongoDB."""
|