feat: per-phase timings of the frame path in the log

One log line per phase every PHASE_LOG_EVERY samples (default 100, 0 = off):
mean and max of wait_frames, align, convert, each JPEG/PNG encoding,
colormap, depth_stats, base64, json and ws_send. Meant for the measurement
on PC2 via `docker logs` (optimize-streams R01): where a capture spends its
~130 ms of CPU is not known yet.

capture() moves its numpy conversion into _frame_from() unchanged, only so
the block can be timed; _depth_colormap() likewise wraps _jet_colormap().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
OliviaCandClaude Opus 5.5 committed 2026-09-29 12:57:25 +02:00
1 parent 441e0bd917
commit 9aa63e4e02
6 files changed
+166 -22

No files matched your search

+3 -1
View File
@@ -5,6 +5,7 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from src.backend.api.routes import _frame_to_dict
from src.backend.services.image_saver import ImageSaver
from src.backend.services.phase_timing import timer
try:
from src.backend.services.detector import Detector
@@ -60,7 +61,8 @@ async def websocket_stream(ws: WebSocket):
data = _frame_to_dict(frame)
if predicting and _detector is not None:
data["detections"] = await asyncio.to_thread(_detector.detect, frame.rgb)
await ws.send_json(data)
with timer.phase("ws_send"):
await ws.send_json(data)
if _saver and _saver.is_recording:
_saver.save_rec_frame(frame.rgb)
await asyncio.sleep(0.033)