@@ -134,6 +134,7 @@ void DeepObjectDetectionNode::declareAndReadParameters()
134134 input_topic_ = this ->declare_parameter <std::string>(" input_topic" , " " );
135135 params_.input_qos_reliability = " best_effort" ;
136136 params_.output_detections_topic = this ->declare_parameter <std::string>(" output_detections_topic" , " /detections" );
137+ output_annotations_topic_ = this ->declare_parameter <std::string>(" output_annotations_topic" , " /image_annotations" );
137138
138139 params_.max_batch_size = this ->declare_parameter <int >(" max_batch_size" , 3 );
139140 params_.queue_size = this ->declare_parameter <int >(" queue_size" , 10 );
@@ -225,6 +226,11 @@ rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn DeepOb
225226 auto pub = this ->create_publisher <Detection2DArrayMsg>(params_.output_detections_topic , rclcpp::SensorDataQoS{});
226227 detection_pub_ = std::static_pointer_cast<rclcpp_lifecycle::LifecyclePublisher<Detection2DArrayMsg>>(pub);
227228
229+ auto marker_pub =
230+ this ->create_publisher <visualization_msgs::msg::ImageMarker>(output_annotations_topic_, rclcpp::SensorDataQoS{});
231+ image_marker_pub_ =
232+ std::static_pointer_cast<rclcpp_lifecycle::LifecyclePublisher<visualization_msgs::msg::ImageMarker>>(marker_pub);
233+
228234 if (input_topic_.empty ()) {
229235 RCLCPP_ERROR (this ->get_logger (), " input_topic is empty. Please set input_topic to a valid MultiImage topic." );
230236 cleanupPartialConfiguration ();
@@ -269,6 +275,10 @@ rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn DeepOb
269275 detection_pub_->on_activate ();
270276 }
271277
278+ if (image_marker_pub_) {
279+ image_marker_pub_->on_activate ();
280+ }
281+
272282 RCLCPP_INFO (this ->get_logger (), " Deep object detection node activated and ready to process images" );
273283 return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
274284 } catch (const std::exception & e) {
@@ -283,6 +293,7 @@ void DeepObjectDetectionNode::cleanupPartialConfiguration()
283293 backend_manager_.reset ();
284294 preprocessor_.reset ();
285295 detection_pub_.reset ();
296+ image_marker_pub_.reset ();
286297}
287298
288299void DeepObjectDetectionNode::cleanupAllResources ()
@@ -293,6 +304,7 @@ void DeepObjectDetectionNode::cleanupAllResources()
293304 postprocessor_.reset ();
294305 params_.class_names .clear ();
295306 detection_pub_.reset ();
307+ image_marker_pub_.reset ();
296308}
297309
298310void DeepObjectDetectionNode::stopSubscriptionsAndTimer ()
@@ -321,6 +333,10 @@ rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn DeepOb
321333 detection_pub_->on_deactivate ();
322334 }
323335
336+ if (image_marker_pub_) {
337+ image_marker_pub_->on_deactivate ();
338+ }
339+
324340 RCLCPP_INFO (this ->get_logger (), " Deep object detection node deactivated" );
325341 return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS;
326342}
@@ -554,6 +570,19 @@ void DeepObjectDetectionNode::publishDetections(
554570 postprocessor_->fillDetectionMessage (headers[i], batch_detections[i], metas[i], msg);
555571 detection_pub_->publish (msg);
556572 total_published += batch_detections[i].size ();
573+
574+ RCLCPP_DEBUG (
575+ this ->get_logger (),
576+ " Published [%zu]: frame_id=%s, %zu detections" ,
577+ i,
578+ headers[i].frame_id .c_str (),
579+ batch_detections[i].size ());
580+
581+ // Also publish ImageMarker annotations if publisher is available
582+ if (image_marker_pub_) {
583+ auto marker_msg = detectionsToImageMarker (headers[i], batch_detections[i]);
584+ image_marker_pub_->publish (marker_msg);
585+ }
557586 }
558587 RCLCPP_INFO_THROTTLE (
559588 this ->get_logger (),
@@ -589,6 +618,86 @@ void DeepObjectDetectionNode::loadClassNames()
589618 this ->get_logger (), " Loaded %zu class names from %s" , params_.class_names .size (), class_names_path.c_str ());
590619}
591620
621+ visualization_msgs::msg::ImageMarker DeepObjectDetectionNode::detectionsToImageMarker (
622+ const std_msgs::msg::Header & header, const std::vector<SimpleDetection> & detections) const
623+ {
624+ visualization_msgs::msg::ImageMarker marker_msg;
625+ marker_msg.header = header;
626+ marker_msg.ns = " detections" ;
627+ marker_msg.id = 0 ;
628+ marker_msg.type = visualization_msgs::msg::ImageMarker::LINE_LIST;
629+ marker_msg.action = visualization_msgs::msg::ImageMarker::ADD;
630+
631+ // Set marker lifetime
632+ marker_msg.lifetime .sec = 0 ;
633+ marker_msg.lifetime .nanosec = 50000000 ; // 0.05 seconds - short lifetime to avoid ghosting
634+ marker_msg.scale = 2.0 ;
635+
636+ // Set outline color to green
637+ marker_msg.outline_color .r = 0.0 ;
638+ marker_msg.outline_color .g = 1.0 ;
639+ marker_msg.outline_color .b = 0.0 ;
640+ marker_msg.outline_color .a = 1.0 ;
641+
642+ // Iterate through detections and add bounding box line segments
643+ for (const auto & det : detections) {
644+ // Skip detections with invalid coordinates
645+ if (det.width <= 0 || det.height <= 0 ) {
646+ continue ;
647+ }
648+
649+ // Calculate bounding box corners
650+ float x_min = det.x ;
651+ float y_min = det.y ;
652+ float x_max = det.x + det.width ;
653+ float y_max = det.y + det.height ;
654+
655+ // Add line points for bounding box rectangle (4 line segments = 8 points)
656+ // Top-left to top-right
657+ geometry_msgs::msg::Point pt1, pt2;
658+ pt1.x = x_min;
659+ pt1.y = y_min;
660+ pt1.z = 0 ;
661+ pt2.x = x_max;
662+ pt2.y = y_min;
663+ pt2.z = 0 ;
664+ marker_msg.points .push_back (pt1);
665+ marker_msg.points .push_back (pt2);
666+
667+ // Top-right to bottom-right
668+ pt1.x = x_max;
669+ pt1.y = y_min;
670+ pt1.z = 0 ;
671+ pt2.x = x_max;
672+ pt2.y = y_max;
673+ pt2.z = 0 ;
674+ marker_msg.points .push_back (pt1);
675+ marker_msg.points .push_back (pt2);
676+
677+ // Bottom-right to bottom-left
678+ pt1.x = x_max;
679+ pt1.y = y_max;
680+ pt1.z = 0 ;
681+ pt2.x = x_min;
682+ pt2.y = y_max;
683+ pt2.z = 0 ;
684+ marker_msg.points .push_back (pt1);
685+ marker_msg.points .push_back (pt2);
686+
687+ // Bottom-left to top-left
688+ pt1.x = x_min;
689+ pt1.y = y_max;
690+ pt1.z = 0 ;
691+ pt2.x = x_min;
692+ pt2.y = y_min;
693+ pt2.z = 0 ;
694+ marker_msg.points .push_back (pt1);
695+ marker_msg.points .push_back (pt2);
696+ }
697+
698+ return marker_msg;
699+ }
700+
592701std::shared_ptr<rclcpp_lifecycle::LifecycleNode> createDeepObjectDetectionNode (const rclcpp::NodeOptions & options)
593702{
594703 return std::make_shared<DeepObjectDetectionNode>(options);
0 commit comments