update at 2026-06-04 14:09:16

This commit is contained in:
陈赣
2026-06-04 14:09:16 +08:00
parent 41bd03123c
commit 4603914e85
11 changed files with 692 additions and 68 deletions

View File

@@ -45,6 +45,18 @@ worker = StreamWorker(
resize_width=settings.resize_width,
)
video_grid_devices = device_manager.get_video_grid_devices()
video_grid_workers = {
device.device_num: StreamWorker(
stream_url=lambda device_num=device.device_num: device_manager.resolve_stream_url_for(device_num),
detector=detector,
frame_skip=settings.frame_skip,
jpeg_quality=settings.jpeg_quality,
resize_width=settings.resize_width,
)
for device in video_grid_devices
}
app = FastAPI(title="DETR 动态打标")
app.mount("/static", StaticFiles(directory="app/static"), name="static")
templates = Jinja2Templates(directory="app/templates")
@@ -57,11 +69,15 @@ def display_model_name(model_name: str) -> str:
@app.on_event("startup")
def startup() -> None:
worker.start()
for grid_worker in video_grid_workers.values():
grid_worker.start()
@app.on_event("shutdown")
def shutdown() -> None:
worker.stop()
for grid_worker in video_grid_workers.values():
grid_worker.stop()
@app.get("/", response_class=HTMLResponse)
@@ -73,15 +89,67 @@ def index(request: Request) -> HTMLResponse:
"model": display_model_name(settings.detr_model),
"device": detector.device_name,
"stream_url": f"设备号:{device_manager.get_snapshot()['current_device_num']}",
"video_grid_devices": video_grid_devices,
},
)
@app.get("/tokenizer", response_class=HTMLResponse)
def tokenizer(request: Request) -> HTMLResponse:
return templates.TemplateResponse(
"tokenizer.html",
{
"request": request,
"model": display_model_name(settings.detr_model),
"device": detector.device_name,
},
)
@app.get("/tokenizer/state")
def tokenizer_state() -> JSONResponse:
snapshot = worker.get_snapshot()
frame = worker.get_frame_rgb()
if frame is None:
return JSONResponse(
{
"ready": False,
"frame_id": snapshot["frame_id"],
"connected": snapshot["connected"],
"error": snapshot["error"] or "等待视频帧",
}
)
data = detector.inspect_tokens(frame)
data.update(
{
"ready": True,
"frame_id": snapshot["frame_id"],
"updated_at": snapshot["updated_at"],
"connected": snapshot["connected"],
"error": snapshot["error"],
}
)
return JSONResponse(data)
@app.get("/video")
def video() -> StreamingResponse:
return stream_video(worker)
@app.get("/video/{device_num}")
def video_device(device_num: str) -> StreamingResponse:
grid_worker = video_grid_workers.get(device_num)
if grid_worker is None:
raise HTTPException(status_code=404, detail="设备不在视频网格中")
return stream_video(grid_worker)
def stream_video(stream_worker: StreamWorker) -> StreamingResponse:
async def generate():
while True:
frame = worker.get_jpeg()
frame = stream_worker.get_jpeg()
if frame is None:
await asyncio.sleep(0.1)
continue
@@ -137,11 +205,13 @@ def status() -> JSONResponse:
@app.post("/devices/{device_num}")
def switch_device(device_num: str) -> JSONResponse:
try:
device_manager.set_current_device(device_num)
version = device_manager.set_current_device(device_num)
except ValueError as exc:
print(f"[device-switch] invalid device={device_num}", flush=True)
raise HTTPException(status_code=404, detail=str(exc)) from exc
worker.reconnect()
return JSONResponse({"current_device_num": device_num})
print(f"[device-switch] accepted device={device_num} version={version}", flush=True)
return JSONResponse({"current_device_num": device_num, "version": version})
@app.websocket("/ws/detections")