Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
Messages
Services
Plugins
Recent questions tagged autoware_image_projection_based_fusion at Robotics Stack Exchange
Package Summary
| Version | 0.53.0 |
| License | Apache License 2.0 |
| Build type | AMENT_CMAKE |
| Use | RECOMMENDED |
Repository Summary
| Description | |
| Checkout URI | https://github.com/autowarefoundation/autoware_universe.git |
| VCS Type | git |
| VCS Version | main |
| Last Updated | 2026-10-27 |
| Dev Status | UNKNOWN |
| Released | UNRELEASED |
| Contributing |
Help Wanted (-)
Good First Issues (-) Pull Requests to Review (-) |
Package Description
Maintainers
- Yukihiro Saito
- Yoshi Ri
- Dai Nguyen
- Kotaro Uetake
- Tao Zhong
- Taekjin Lee
- Masaki Baba
Authors
autoware_image_projection_based_fusion
Purpose
The autoware_image_projection_based_fusion package is designed to enhance obstacle detection accuracy by integrating information from both image-based and LiDAR-based perception. It fuses detected obstacles — such as bounding boxes or segmentation — from 2D images with 3D point clouds or other obstacle representations, including bounding boxes, clusters, or segmentation. This fusion helps to refine obstacle classification and detection in autonomous driving applications.
Fusion algorithms
The package provides multiple fusion algorithms, each designed for specific use cases. Below are the different fusion methods along with their descriptions and detailed documentation links:
| Fusion Name | Description | Detail |
|---|---|---|
| roi_cluster_fusion | Assigns classification labels to LiDAR-detected clusters by matching them with Regions of Interest (ROIs) from a 2D object detector. | link |
| roi_detected_object_fusion | Updates classification labels of detected objects using ROI information from a 2D object detector. | link |
| pointpainting_fusion | Augments the point cloud by painting each point with additional information from ROIs of a 2D object detector. The enriched point cloud is then processed by a 3D object detector for improved accuracy. | link |
| roi_pointcloud_fusion | Matching pointcloud with ROIs from a 2D object detector to detect unknown-labeled objects. | link |
| segmentation_pointcloud_fusion | Filtering pointcloud that are belong to less interesting region which is defined by semantic or instance segmentation by 2D image segmentation. | link |
Inner Workings / Algorithms
The fusion process operates on two primary types of input data:
- Msg3d: This includes 3D data such as point clouds, bounding boxes, or clusters from LiDAR.
- RoIs (Regions of Interest): These are 2D detections or proposals from camera-based perception modules, such as object detection bounding boxes.
Both inputs come with timestamps, which are crucial for synchronization and fusion. Since sensors operate at different frequencies and may experience network delays, a systematic approach is needed to handle their arrival, align their timestamps, and ensure reliable fusion.
The following steps describe how the node processes these inputs, synchronizes them, and performs multi-sensor fusion.
Step 1: Matching and Creating a Collector
When a Msg3d or a set of RoIs arrives, its timestamp is checked, and an offset is subtracted to determine the reference timestamp. The node then searches for an existing collector with the same reference timestamp.
- If a matching collector is found, the incoming data is added to it.
- If no matching collector exists, a new collector is created and initialized with the reference timestamp.
Step 2: Triggering the Timer
Once a collector is created, a countdown timer is started. The timeout duration depends on which message type arrived first and is defined by either msg3d_timeout_sec for msg3d or rois_timeout_sec for RoIs.
The collector will attempt to fuse the collected 3D and 2D data either:
- When both Msg3d and RoI data are available, or
- When the timer expires.
If no Msg3d is received before the timer expires, the collector will discard the data without performing fusion.
Step 3: Fusion Process
The fusion process consists of three main stages:
- Preprocessing – Preparing the input data for fusion.
- Fusion – Aligning and merging RoIs with the 3D point cloud.
- Postprocessing – Refining the fused output based on the algorithm’s requirements.
The specific operations performed during these stages may vary depending on the type of fusion being applied.
Step 4: Publishing the Fused Result
After the fusion process is completed, the fused output is published. The collector is then reset to an idle state, ready to process the next incoming message.
The figure below shows how the input data is fused in different scenarios.

Parameters
All of the fusion nodes have the common parameters described in the following
{{ json_to_markdown(“perception/autoware_image_projection_based_fusion/schema/fusion_common.schema.json”) }}
Parameter Settings
Timeout
The order in which RoIs or the msg3d message arrives at the fusion node depends on your system and sensor configuration. Since the primary goal is to fuse 2D RoIs with msg3d data, msg3d is essential for processing.
If RoIs arrive earlier, they must wait until msg3d is received. You can adjust the waiting time using the rois_timeout_sec parameter.
If msg3d arrives first, the fusion process should proceed as quickly as possible, so the waiting time for msg3d (msg3d_timeout_sec) should be kept minimal.
RoIs Offsets
The offset between each camera and the LiDAR is determined by their shutter timing. To ensure accurate fusion, users must understand the timing offset between the RoIs and msg3d. Once this offset is known, it should be specified in the parameter rois_timestamp_offsets.
In the figure below, the LiDAR completes a full scan from the rear in 100 milliseconds. When the LiDAR scan reaches the area where the camera is facing, the camera is triggered, capturing an image with a corresponding timestamp. The rois_timestamp_offsets can then be calculated by subtracting the LiDAR header timestamp from the camera header timestamp. As a result, the rois_timestamp_offsets would be [0.059, 0.010, 0.026, 0.042, 0.076, 0.093].
To check the header timestamp of the msg3d and RoIs, user can easily run
ros2 echo [topic] --header field
Matching Strategies
We provide two matching strategies for different scenarios:
File truncated at 100 lines see the full file
Changelog for package autoware_image_projection_based_fusion
0.53.0 (2026-09-29)
-
Merge remote-tracking branch 'origin/main' into prepare-0.53.0-changelog
-
fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown (#13399)
* fix(autoware_image_projection_based_fusion): use runtime-agnostic wait and shutdown An executable registered with an AgnocastOnly* executor brings up only the agnocast context, so rclcpp::ok() is false and rclcpp::shutdown() is a no-op when ENABLE_AGNOCAST=1.
- camera_info_callback(): the logging thread is joined right after the projector is built, so [initializing]{.title-ref} alone terminates the loop. Drop rclcpp::ok() and rclcpp::Rate, which also avoids rclcpp::Rate::sleep() throwing on an invalid context.
- pointpainting: call autoware::agnocast_wrapper::shutdown() so build_only terminates the node in both modes.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in the standalone launch files Every node in this package is registered with an AgnocastOnly* executor, which needs libagnocast_heaphook.so in LD_PRELOAD. Only roi_cluster_fusion included agnocast_env.launch.xml, so the other standalone nodes exited immediately at ENABLE_AGNOCAST=1 with "libagnocast_heaphook.so not found in LD_PRELOAD". roi_pointcloud_fusion is left alone: it only loads into an existing container, which owns LD_PRELOAD.
* fix(autoware_image_projection_based_fusion): set LD_PRELOAD in segmentation_pointcloud_fusion.launch.py ---------
-
fix(image_projection_based_fusion): dangling reference bug fix (#13266)
-
feat(image_projection_based_fusion): apply [agnocast_wrapper::Node]{.title-ref} to RoiFusion base node (#12688)
- apply agnocast_wrapper::Node
- fix
- style(pre-commit): autofix
- refactor(image_projection_based_fusion): drop node name cache, get_name() returns const char*
* fix(image_projection_based_fusion): publish debug images without image_transport image_transport needs a real rclcpp::Node, so the debug publishers could not be created from the wrapper node and debug_mode/is_publish_debug_mask had to be forced off under agnocast. Passing [this]{.title-ref} to image_transport in the non-agnocast path also failed to compile once agnocast_wrapper::Node stopped being an rclcpp::Node alias. Use plain sensor_msgs/Image publishers and subscriptions instead, as blockage_diag does. The debug topics no longer offer compressed transports, and the image_transport-specific format / jpeg_quality / png_level parameters are gone. Also add the missing <utility> include flagged by cpplint.
- use AUTOWARE_MESSAGE_CONST_SHARED_PTR for image_buffer_
* fix(image_projection_based_fusion): keep the lock_guard in FusionCollector::set_info/get_info The guard was dropped because this PR merges the Agnocast callback group back into the single default one, restoring the pre-Agnocast callback layout. That makes the accesses serialized in practice today, but it relies on the executor / callback group configuration rather than on anything local to FusionCollector, and #12439 already changed that assumption once. fusion_collector_info_ is a shared_ptr written from the subscription callbacks and read from the timer callback, so keep the guard to stay robust if the callback groups are split again.
* fix(image_projection_based_fusion): declare autoware_utils_debug and autoware_utils_diagnostics ---------Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
-
refactor(pointpainting-fusion): replace NMS with perception_utils::IouBevNms (#13180) refactor: replace NMS with perception_utils::IouBevNms
-
chore(pre-commit): update clang-format to v22.1.5 (#13126)
- chore(pre-commit): update clang-format to v22.1.5
* style(pre-commit): autofix ---------
-
fix(autoware_image_projection_based_fusion): fix bug for contention which was introduced when [callback_group]{.title-ref} was splitted (#13011)
- fix bug for contention when callback_group was splitted
File truncated at 100 lines see the full file
Package Dependencies
System Dependencies
Dependant Packages
| Name | Deps |
|---|---|
| autoware_perception_launch |
Launch files
- launch/pointpainting_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /sensing/lidar/top/rectified/pointcloud]
- output/objects [default: objects]
- data_path [default: $(env HOME)/autoware_data/ml_models]
- model_name [default: pointpainting]
- model_path [default: $(var data_path)/image_projection_based_fusion]
- model_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting.param.yaml]
- ml_package_param_path [default: $(var model_path)/$(var model_name)_ml_package.param.yaml]
- class_remapper_param_path [default: $(var model_path)/detection_class_remapper.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- common_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/pointpainting_common.param.yaml]
- build_only [default: false]
- use_pointcloud_container [default: false]
- pointcloud_container_name [default: pointcloud_container]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_cluster_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/clusters [default: clusters]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: labeled_clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_cluster_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_detected_object_fusion.launch.xml
-
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/objects [default: objects]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/objects [default: fused_objects]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_detected_object_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/roi_pointcloud_fusion.launch.xml
-
- pointcloud_container_name [default: pointcloud_container]
- input/rois_number [default: 6]
- input/rois0 [default: rois0]
- input/camera_info0 [default: /camera_info0]
- input/rois1 [default: rois1]
- input/camera_info1 [default: /camera_info1]
- input/rois2 [default: rois2]
- input/camera_info2 [default: /camera_info2]
- input/rois3 [default: rois3]
- input/camera_info3 [default: /camera_info3]
- input/rois4 [default: rois4]
- input/camera_info4 [default: /camera_info4]
- input/rois5 [default: rois5]
- input/camera_info5 [default: /camera_info5]
- input/rois6 [default: rois6]
- input/camera_info6 [default: /camera_info6]
- input/rois7 [default: rois7]
- input/camera_info7 [default: /camera_info7]
- input/rois8 [default: rois8]
- input/camera_info8 [default: /camera_info8]
- input/pointcloud [default: /perception/object_recognition/detection/pointcloud_map_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/clusters [default: output/clusters]
- debug/clusters [default: roi_pointcloud_fusion/debug/clusters]
- param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/roi_pointcloud_fusion.param.yaml]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- input_rois_number [default: $(var input/rois_number)]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]
- launch/segmentation_pointcloud_fusion.launch.xml
-
- input/camera_number [default: 1]
- input/mask0 [default: /perception/object_recognition/detection/mask0]
- input/camera_info0 [default: /sensing/camera/camera0/camera_info]
- input/mask1 [default: /perception/object_recognition/detection/mask1]
- input/camera_info1 [default: /sensing/camera/camera1/camera_info]
- input/mask2 [default: /perception/object_recognition/detection/mask2]
- input/camera_info2 [default: /sensing/camera/camera2/camera_info]
- input/mask3 [default: /perception/object_recognition/detection/mask3]
- input/camera_info3 [default: /sensing/camera/camera3/camera_info]
- input/mask4 [default: /perception/object_recognition/detection/mask4]
- input/camera_info4 [default: /sensing/camera/camera4/camera_info]
- input/mask5 [default: /perception/object_recognition/detection/mask5]
- input/camera_info5 [default: /sensing/camera/camera5/camera_info]
- input/mask6 [default: /perception/object_recognition/detection/mask6]
- input/camera_info6 [default: /sensing/camera/camera6/camera_info]
- input/mask7 [default: /perception/object_recognition/detection/mask7]
- input/camera_info7 [default: /sensing/camera/camera7/camera_info]
- input/mask8 [default: /perception/object_recognition/detection/mask8]
- input/camera_info8 [default: /sensing/camera/camera8/camera_info]
- input/pointcloud [default: /sensing/lidar/top/outlier_filtered/pointcloud]
- input/concatenation_info [default: /sensing/lidar/concatenated/pointcloud_info]
- output/pointcloud [default: output/pointcloud]
- sync_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/fusion_common.param.yaml]
- semantic_segmentation_based_filter_param_path [default: $(find-pkg-share autoware_image_projection_based_fusion)/config/segmentation_pointcloud_fusion.param.yaml]
- input/image0 [default: /image_raw0]
- input/image1 [default: /image_raw1]
- input/image2 [default: /image_raw2]
- input/image3 [default: /image_raw3]
- input/image4 [default: /image_raw4]
- input/image5 [default: /image_raw5]
- input/image6 [default: /image_raw6]
- input/image7 [default: /image_raw7]
- input/image8 [default: /image_raw8]