@@ -25,18 +25,18 @@ def __init__(self, camera: BaseCamera | None = None):
2525 if camera is None :
2626 camera = Camera (fps = 30 )
2727 self .camera = camera
28-
28+
2929 # Callbacks
3030 self ._gesture_callbacks = {} # {(gesture, hand): callback}
3131 self ._enter_callback = None
3232 self ._exit_callback = None
3333 self ._frame_callback = None
3434 self ._callbacks_lock = threading .Lock ()
35-
35+
3636 # State tracking
3737 self ._had_hands = False
3838 self ._running = False
39-
39+
4040 self ._camera_frame_queue = queue .Queue (maxsize = 2 )
4141
4242 # WebSocket endpoints
@@ -50,22 +50,22 @@ def __init__(self, camera: BaseCamera | None = None):
5050 self ._host = resolve_address (self ._host )
5151 if not self ._host :
5252 raise RuntimeError ("Host address could not be resolved. Please check your configuration." )
53-
53+
5454 self ._ws_send_url = f"ws://{ self ._host } :5050"
5555 self ._ws_recv_url = f"ws://{ self ._host } :5051"
56-
56+
5757 def start (self ):
5858 """Start the capture thread and asyncio event loop."""
5959 self ._running = True
60-
60+
6161 def stop (self ):
6262 """Stop all tracking and close connections."""
6363 self ._running = False
6464
6565 def on_gesture (self , gesture : str , callback : Callable [[dict ], None ], hand : Literal ["left" , "right" , "both" ] = "both" ):
6666 """
6767 Register or unregister a gesture callback.
68-
68+
6969 Args:
7070 gesture (str): The gesture name to detect
7171 callback (Callable[[dict], None]): Function to call when gesture is detected. None to unregister.
@@ -77,7 +77,7 @@ def on_gesture(self, gesture: str, callback: Callable[[dict], None], hand: Liter
7777 x and y are pixel coordinates and z is normalized depth)
7878 - "bounding_box_xyxy": [x_min, y_min, x_max, y_max] of the detected hand bounding box
7979 hand (Literal["left", "right", "both"]): Which hand(s) to track
80-
80+
8181 Raises:
8282 ValueError: If 'hand' argument is not valid
8383 """
@@ -95,7 +95,7 @@ def on_gesture(self, gesture: str, callback: Callable[[dict], None], hand: Liter
9595 def on_enter (self , callback : Callable [[], None ]):
9696 """
9797 Register a callback for when hands become visible.
98-
98+
9999 Args:
100100 callback (Callable[[], None]): Function to call when at least one hand is detected
101101 """
@@ -105,7 +105,7 @@ def on_enter(self, callback: Callable[[], None]):
105105 def on_exit (self , callback : Callable [[], None ]):
106106 """
107107 Register a callback for when hands are no longer visible.
108-
108+
109109 Args:
110110 callback (Callable[[], None]): Function to call when no hands are detected anymore
111111 """
@@ -115,13 +115,13 @@ def on_exit(self, callback: Callable[[], None]):
115115 def on_frame (self , callback : Callable [[np .ndarray ], None ]):
116116 """
117117 Register a callback that receives each camera frame.
118-
118+
119119 Args:
120120 callback (Callable[[np.ndarray], None]): Function to call with camera frame data. None to unregister.
121121 """
122122 with self ._callbacks_lock :
123123 self ._frame_callback = callback
124-
124+
125125 @brick .loop
126126 def _capture_loop (self ):
127127 """Continuously capture frames from camera (runs in dedicated thread)."""
@@ -130,15 +130,15 @@ def _capture_loop(self):
130130 if frame is None :
131131 time .sleep (0.01 )
132132 return
133-
133+
134134 with self ._callbacks_lock :
135135 frame_cb = self ._frame_callback
136136 if frame_cb :
137137 try :
138138 frame_cb (frame )
139139 except Exception as e :
140140 print (f"Error in frame callback: { e } " )
141-
141+
142142 jpeg_frame = compress_to_jpeg (frame )
143143 if jpeg_frame is None :
144144 time .sleep (0.01 )
@@ -153,57 +153,47 @@ def _capture_loop(self):
153153 self ._camera_frame_queue .put (jpeg_frame , block = False )
154154 except :
155155 pass
156-
156+
157157 except Exception as e :
158158 if self ._running :
159159 print (f"Error capturing frame: { e } " )
160-
160+
161161 @brick .execute
162162 def _send_receive_loop (self ):
163163 """Run the asyncio event loop in a dedicated thread."""
164164 loop = asyncio .new_event_loop ()
165165 asyncio .set_event_loop (loop )
166-
166+
167167 try :
168- tasks = asyncio .gather (
169- self ._send_frames_task (),
170- self ._receive_detections_task (),
171- return_exceptions = True
172- )
168+ tasks = asyncio .gather (self ._send_frames_task (), self ._receive_detections_task (), return_exceptions = True )
173169 loop .run_until_complete (tasks )
174170
175171 except Exception as e :
176172 print (f"Error in asyncio loop: { e } " )
177173 finally :
178174 loop .close ()
179-
175+
180176 async def _send_frames_task (self ):
181177 """Send frames to the processing container via WebSocket."""
182178 while self ._running :
183179 try :
184180 async with websockets .connect (self ._ws_send_url ) as ws :
185181 while self ._running :
186182 try :
187- frame = await asyncio .get_event_loop ().run_in_executor (
188- None , self ._camera_frame_queue .get , True , 0.1
189- )
183+ frame = await asyncio .get_event_loop ().run_in_executor (None , self ._camera_frame_queue .get , True , 0.1 )
190184 except queue .Empty :
191185 continue
192-
193- b64_frame = base64 .b64encode (frame .tobytes ()).decode ('utf-8' )
194- payload = {
195- "frame" : b64_frame ,
196- "width" : 640 ,
197- "height" : 480
198- }
199-
186+
187+ b64_frame = base64 .b64encode (frame .tobytes ()).decode ("utf-8" )
188+ payload = {"frame" : b64_frame , "width" : 640 , "height" : 480 }
189+
200190 await ws .send (json .dumps (payload ))
201-
191+
202192 except Exception as e :
203193 if self ._running :
204194 print (f"Error in send frames task: { e } . Reconnecting..." )
205195 await asyncio .sleep (3 )
206-
196+
207197 async def _receive_detections_task (self ):
208198 """Receive detection results and dispatch events."""
209199 while self ._running :
@@ -212,71 +202,71 @@ async def _receive_detections_task(self):
212202 while self ._running :
213203 data = await ws .recv ()
214204 detection = json .loads (data )
215-
216- self ._process_detection (detection .get (' metadata' , {}))
217-
205+
206+ self ._process_detection (detection .get (" metadata" , {}))
207+
218208 except Exception as e :
219209 if self ._running :
220210 print (f"Error in receive detections task: { e } . Reconnecting..." )
221211 await asyncio .sleep (3 )
222-
212+
223213 def _process_detection (self , metadata : dict ):
224214 """Process detection data and dispatch appropriate events."""
225- hands_data = metadata .get (' hands' , [])
215+ hands_data = metadata .get (" hands" , [])
226216 has_hands = bool (hands_data )
227-
217+
228218 # Dispatch hand enter/exit events
229219 if has_hands and not self ._had_hands :
230220 self ._dispatch_enter ()
231221 elif self ._had_hands and not has_hands :
232222 self ._dispatch_exit ()
233-
223+
234224 self ._had_hands = has_hands
235-
225+
236226 # Dispatch hand gesture events
237227 for hand_data in hands_data :
238- hand = hand_data .get (' hand' , '' )
239- gesture = hand_data .get (' gesture' , '' )
240- if hand in (' left' , ' right' ) and gesture :
228+ hand = hand_data .get (" hand" , "" )
229+ gesture = hand_data .get (" gesture" , "" )
230+ if hand in (" left" , " right" ) and gesture :
241231 self ._dispatch_gesture (gesture , hand , metadata )
242-
232+
243233 def _dispatch_enter (self ):
244234 """Dispatch hand enter event."""
245235 with self ._callbacks_lock :
246236 callback = self ._enter_callback
247-
237+
248238 if callback :
249239 try :
250240 callback ()
251241 except Exception as e :
252242 print (f"Error in enter callback: { e } " )
253-
243+
254244 def _dispatch_exit (self ):
255245 """Dispatch hand exit event."""
256246 with self ._callbacks_lock :
257247 callback = self ._exit_callback
258-
248+
259249 if callback :
260250 try :
261251 callback ()
262252 except Exception as e :
263253 print (f"Error in exit callback: { e } " )
264-
254+
265255 def _dispatch_gesture (self , gesture : str , hand : Literal ["left" , "right" ], metadata : dict ):
266256 """Dispatch gesture event to registered callbacks."""
267257 callbacks_to_call = []
268-
258+
269259 with self ._callbacks_lock :
270260 # Check for exact hand match
271261 exact_key = (gesture , hand )
272262 if exact_key in self ._gesture_callbacks :
273263 callbacks_to_call .append (self ._gesture_callbacks [exact_key ])
274-
264+
275265 # Check for "both" wildcard
276266 both_key = (gesture , "both" )
277267 if both_key in self ._gesture_callbacks :
278268 callbacks_to_call .append (self ._gesture_callbacks [both_key ])
279-
269+
280270 for callback in callbacks_to_call :
281271 try :
282272 callback (metadata )
0 commit comments