33# SPDX-License-Identifier: MIT
44import asyncio
55import json
6+ import cv2
67import logging
78
89import numpy as np
@@ -37,6 +38,15 @@ def __init__(self) -> None:
3738 self ._processing_task : asyncio .Task [None ] | None = None
3839 self ._intrinsics_logged : bool = False
3940
41+ self .max_consecutive_errors = 5
42+ # adaptive downscaling parameters
43+ self .target_scale_init = config .TARGET_SCALE_INIT
44+ self .smooth_factor = config .SMOOTH_FACTOR
45+ self .min_scale = config .MIN_SCALE
46+ self .max_scale = config .MAX_SCALE
47+ # adaptive frame dropping parameters
48+ self .fps_threshold = config .FPS_THRESHOLD
49+
4050 async def connect (self , websocket : WebSocket ) -> None :
4151 """Accept a new WebSocket connection."""
4252 await websocket .accept ()
@@ -107,9 +117,10 @@ async def _process_frames(self, source_track: MediaStreamTrack) -> None:
107117 frame_id = 0
108118 last_fps_time = asyncio .get_event_loop ().time ()
109119 fps_counter = 0
110- current_fps = 0 .0
120+ current_fps = 30 .0
111121 consecutive_errors = 0
112- max_consecutive_errors = 5
122+
123+ target_scale = self .target_scale_init
113124
114125 try :
115126 while self .active_connections :
@@ -121,7 +132,8 @@ async def _process_frames(self, source_track: MediaStreamTrack) -> None:
121132 except asyncio .TimeoutError :
122133 logging .warning ("Frame receive timeout, skipping..." )
123134 consecutive_errors += 1
124- if consecutive_errors >= max_consecutive_errors :
135+
136+ if consecutive_errors >= self .max_consecutive_errors :
125137 logging .error (
126138 "Too many consecutive timeouts, reconnecting..."
127139 )
@@ -133,7 +145,7 @@ async def _process_frames(self, source_track: MediaStreamTrack) -> None:
133145 )
134146 consecutive_errors += 1
135147
136- if consecutive_errors >= max_consecutive_errors :
148+ if consecutive_errors >= self . max_consecutive_errors :
137149 # full reconnect
138150 if self ._webcam_session is not None :
139151 await self ._webcam_session .close ()
@@ -170,21 +182,45 @@ async def _process_frames(self, source_track: MediaStreamTrack) -> None:
170182 fps_counter = 0
171183 last_fps_time = current_time
172184
185+ if current_fps < 10 :
186+ target_scale -= self .smooth_factor
187+ elif current_fps < 18 :
188+ target_scale -= self .smooth_factor * 0.5
189+ else :
190+ target_scale += self .smooth_factor * 0.8
191+
192+ target_scale = max (
193+ self .min_scale , min (self .max_scale , target_scale )
194+ )
195+ print (
196+ f"[Adaptive Res] Scale={ target_scale :.2f} | FPS={ current_fps :.1f} "
197+ )
198+
199+ # Resize frame for processing
200+ if target_scale < 0.98 :
201+ new_w = int (frame_array .shape [1 ] * target_scale )
202+ new_h = int (frame_array .shape [0 ] * target_scale )
203+ frame_small = cv2 .resize (frame_array , (new_w , new_h ))
204+ else :
205+ frame_small = frame_array
206+
207+ sample_rate = 2 if current_fps < self .fps_threshold else 4
208+
173209 # Run ML inference every 3rd frame and collect detections
174- if not self .active_connections or frame_id % 3 != 0 :
210+ if not self .active_connections or frame_id % sample_rate != 0 :
175211 continue
176212
177213 # YOLO detection
178- detections = await detector .infer (frame_array )
214+ detections = await detector .infer (frame_small )
179215
180216 if not detections :
181217 continue
182218
183219 # Distance estimation
184- distances = estimator .estimate_distance_m (frame_array , detections )
220+ distances = estimator .estimate_distance_m (frame_small , detections )
185221
186222 metadata = self ._build_metadata_message (
187- frame_rgb = frame_array ,
223+ frame_rgb = frame_small ,
188224 detections = detections ,
189225 distances = distances ,
190226 timestamp = current_time ,
@@ -196,7 +232,7 @@ async def _process_frames(self, source_track: MediaStreamTrack) -> None:
196232 await self ._send_metadata (metadata )
197233
198234 # Small delay to prevent overwhelming
199- await asyncio .sleep (0.033 ) # ~30 FPS processing
235+ # await asyncio.sleep(0.033) # ~30 FPS processing
200236
201237 except Exception as e :
202238 logging .warning (f"Frame processing error: { e } " )
0 commit comments