4444
4545
4646class DebugNode (LifecycleNode ):
47+ """
48+ ROS 2 Lifecycle Node for visualizing YOLO detections.
49+
50+ This node subscribes to images and detections, rendering bounding boxes,
51+ masks, keypoints, and 3D markers for debugging and visualization purposes.
52+ """
4753
4854 def __init__ (self ) -> None :
55+ """
56+ Initialize the debug node.
57+
58+ Sets up color mapping and declares ROS parameters.
59+ """
4960 super ().__init__ ("debug_node" )
5061
5162 self ._class_to_color = {}
5263 self .cv_bridge = CvBridge ()
5364
54- # params
65+ # Params
5566 self .declare_parameter ("image_reliability" , QoSReliabilityPolicy .BEST_EFFORT )
5667
5768 def on_configure (self , state : LifecycleState ) -> TransitionCallbackReturn :
69+ """
70+ Configure lifecycle callback.
71+
72+ Retrieves parameters and creates publishers for debug visualizations.
73+
74+ @param state Current lifecycle state
75+ @return Transition callback return status
76+ """
5877 self .get_logger ().info (f"[{ self .get_name ()} ] Configuring..." )
5978
6079 self .image_qos_profile = QoSProfile (
@@ -66,7 +85,7 @@ def on_configure(self, state: LifecycleState) -> TransitionCallbackReturn:
6685 depth = 1 ,
6786 )
6887
69- # pubs
88+ # Pubs
7089 self ._dbg_pub = self .create_publisher (Image , "dbg_image" , 10 )
7190 self ._bb_markers_pub = self .create_publisher (MarkerArray , "dgb_bb_markers" , 10 )
7291 self ._kp_markers_pub = self .create_publisher (MarkerArray , "dgb_kp_markers" , 10 )
@@ -77,9 +96,17 @@ def on_configure(self, state: LifecycleState) -> TransitionCallbackReturn:
7796 return TransitionCallbackReturn .SUCCESS
7897
7998 def on_activate (self , state : LifecycleState ) -> TransitionCallbackReturn :
99+ """
100+ Activate lifecycle callback.
101+
102+ Creates subscriptions to image and detection topics with time synchronization.
103+
104+ @param state Current lifecycle state
105+ @return Transition callback return status
106+ """
80107 self .get_logger ().info (f"[{ self .get_name ()} ] Activating..." )
81108
82- # subs
109+ # Subs
83110 self .image_sub = message_filters .Subscriber (
84111 self , Image , "image_raw" , qos_profile = self .image_qos_profile
85112 )
@@ -98,6 +125,14 @@ def on_activate(self, state: LifecycleState) -> TransitionCallbackReturn:
98125 return TransitionCallbackReturn .SUCCESS
99126
100127 def on_deactivate (self , state : LifecycleState ) -> TransitionCallbackReturn :
128+ """
129+ Deactivate lifecycle callback.
130+
131+ Destroys subscriptions and cleans up the synchronizer.
132+
133+ @param state Current lifecycle state
134+ @return Transition callback return status
135+ """
101136 self .get_logger ().info (f"[{ self .get_name ()} ] Deactivating..." )
102137
103138 self .destroy_subscription (self .image_sub .sub )
@@ -111,6 +146,14 @@ def on_deactivate(self, state: LifecycleState) -> TransitionCallbackReturn:
111146 return TransitionCallbackReturn .SUCCESS
112147
113148 def on_cleanup (self , state : LifecycleState ) -> TransitionCallbackReturn :
149+ """
150+ Cleanup lifecycle callback.
151+
152+ Destroys publishers and cleans up resources.
153+
154+ @param state Current lifecycle state
155+ @return Transition callback return status
156+ """
114157 self .get_logger ().info (f"[{ self .get_name ()} ] Cleaning up..." )
115158
116159 self .destroy_publisher (self ._dbg_pub )
@@ -123,6 +166,14 @@ def on_cleanup(self, state: LifecycleState) -> TransitionCallbackReturn:
123166 return TransitionCallbackReturn .SUCCESS
124167
125168 def on_shutdown (self , state : LifecycleState ) -> TransitionCallbackReturn :
169+ """
170+ Shutdown lifecycle callback.
171+
172+ Performs final cleanup before node shutdown.
173+
174+ @param state Current lifecycle state
175+ @return Transition callback return status
176+ """
126177 self .get_logger ().info (f"[{ self .get_name ()} ] Shutting down..." )
127178 super ().on_shutdown (state )
128179 self .get_logger ().info (f"[{ self .get_name ()} ] Shutted down" )
@@ -134,8 +185,18 @@ def draw_box(
134185 detection : Detection ,
135186 color : Tuple [int ],
136187 ) -> np .ndarray :
188+ """
189+ Draw a bounding box on the image.
190+
191+ Renders a rotated rectangle with class name, track ID, and confidence score.
192+
193+ @param cv_image OpenCV image to draw on
194+ @param detection Detection message containing box information
195+ @param color RGB color tuple for the box
196+ @return Modified image with drawn bounding box
197+ """
137198
138- # get detection info
199+ # Get detection info
139200 class_name = detection .class_name
140201 score = detection .score
141202 box_msg : BoundingBox2D = detection .bbox
@@ -150,7 +211,7 @@ def draw_box(
150211 round (box_msg .center .position .y + box_msg .size .y / 2.0 ),
151212 )
152213
153- # define the four corners of the rectangle
214+ # Define the four corners of the rectangle
154215 rect_pts = np .array (
155216 [
156217 [min_pt [0 ], min_pt [1 ]],
@@ -160,14 +221,14 @@ def draw_box(
160221 ]
161222 )
162223
163- # calculate the rotation matrix
224+ # Calculate the rotation matrix
164225 rotation_matrix = cv2 .getRotationMatrix2D (
165226 (box_msg .center .position .x , box_msg .center .position .y ),
166227 - np .rad2deg (box_msg .center .theta ),
167228 1.0 ,
168229 )
169230
170- # rotate the corners of the rectangle
231+ # Rotate the corners of the rectangle
171232 rect_pts = np .int0 (cv2 .transform (np .array ([rect_pts ]), rotation_matrix )[0 ])
172233
173234 # Draw the rotated rectangle
@@ -176,7 +237,7 @@ def draw_box(
176237 pt2 = tuple (rect_pts [(i + 1 ) % 4 ])
177238 cv2 .line (cv_image , pt1 , pt2 , color , 2 )
178239
179- # write text
240+ # Write text
180241 label = f"{ class_name } "
181242 label += f" ({ track_id } )" if track_id else ""
182243 label += " ({:.3f})" .format (score )
@@ -192,6 +253,16 @@ def draw_mask(
192253 detection : Detection ,
193254 color : Tuple [int ],
194255 ) -> np .ndarray :
256+ """
257+ Draw a segmentation mask on the image.
258+
259+ Renders the mask as a semi-transparent filled polygon.
260+
261+ @param cv_image OpenCV image to draw on
262+ @param detection Detection message containing mask information
263+ @param color RGB color tuple for the mask
264+ @return Modified image with drawn mask
265+ """
195266
196267 mask_msg = detection .mask
197268 mask_array = np .array ([[int (ele .x ), int (ele .y )] for ele in mask_msg .data ])
@@ -211,6 +282,15 @@ def draw_mask(
211282 return cv_image
212283
213284 def draw_keypoints (self , cv_image : np .ndarray , detection : Detection ) -> np .ndarray :
285+ """
286+ Draw keypoints and skeleton on the image.
287+
288+ Renders individual keypoints as circles and connects them with skeleton lines.
289+
290+ @param cv_image OpenCV image to draw on
291+ @param detection Detection message containing keypoint information
292+ @return Modified image with drawn keypoints and skeleton
293+ """
214294
215295 keypoints_msg = detection .keypoints
216296
@@ -266,6 +346,13 @@ def get_pk_pose(kp_id: int) -> Tuple[int]:
266346 return cv_image
267347
268348 def create_bb_marker (self , detection : Detection , color : Tuple [int ]) -> Marker :
349+ """
350+ Create a 3D bounding box marker for RViz visualization.
351+
352+ @param detection Detection message containing 3D bbox information
353+ @param color RGB color tuple for the marker
354+ @return Marker message for visualization
355+ """
269356
270357 bbox3d = detection .bbox3d
271358
@@ -300,6 +387,12 @@ def create_bb_marker(self, detection: Detection, color: Tuple[int]) -> Marker:
300387 return marker
301388
302389 def create_kp_marker (self , keypoint : KeyPoint3D ) -> Marker :
390+ """
391+ Create a 3D keypoint marker for RViz visualization.
392+
393+ @param keypoint 3D keypoint to visualize
394+ @return Marker message for visualization
395+ """
303396
304397 marker = Marker ()
305398
@@ -331,14 +424,23 @@ def create_kp_marker(self, keypoint: KeyPoint3D) -> Marker:
331424 return marker
332425
333426 def detections_cb (self , img_msg : Image , detection_msg : DetectionArray ) -> None :
427+ """
428+ Synchronized callback for image and detections.
429+
430+ Processes detections and creates debug visualizations including annotated
431+ images and 3D markers for bounding boxes and keypoints.
432+
433+ @param img_msg Image message
434+ @param detection_msg Detections message
435+ """
334436 cv_image = self .cv_bridge .imgmsg_to_cv2 (img_msg , desired_encoding = "bgr8" )
335437 bb_marker_array = MarkerArray ()
336438 kp_marker_array = MarkerArray ()
337439
338440 detection : Detection
339441 for detection in detection_msg .detections :
340442
341- # random color
443+ # Random color
342444 class_name = detection .class_name
343445
344446 if class_name not in self ._class_to_color :
@@ -367,7 +469,7 @@ def detections_cb(self, img_msg: Image, detection_msg: DetectionArray) -> None:
367469 marker .id = len (kp_marker_array .markers )
368470 kp_marker_array .markers .append (marker )
369471
370- # publish dbg image
472+ # Publish dbg image
371473 self ._dbg_pub .publish (
372474 self .cv_bridge .cv2_to_imgmsg (cv_image , encoding = "bgr8" , header = img_msg .header )
373475 )
0 commit comments