From 9afa3eb78bb3026f3bd9e0c802d3de6d557e2ca9 Mon Sep 17 00:00:00 2001 From: Jaiveer Singh Date: Fri, 31 May 2024 08:35:58 +0000 Subject: [PATCH] Isaac ROS 3.0.0 --- README.md | 21 +- isaac_ros_dnn_image_encoder/CMakeLists.txt | 10 +- .../config/dnn_image_encoder_node.yaml | 13 +- .../dnn_image_encoder_node.hpp | 2 +- .../launch/dnn_image_encoder.launch.py | 288 +++++++ isaac_ros_dnn_image_encoder/package.xml | 9 +- .../src/dnn_image_encoder_node.cpp | 6 +- ...c_ros_dnn_image_encoder_image_crop_test.py | 62 +- ...c_ros_dnn_image_encoder_image_norm_test.py | 61 +- .../test/isaac_ros_dnn_image_encoder_test.py | 58 +- .../pose_estimation_0/camera_info.json | 51 ++ .../gxf_isaac_tensor_rt}/CMakeLists.txt | 47 +- .../tensor_rt/tensor_rt_extension.cpp | 6 +- .../tensor_rt/tensor_rt_inference.cpp | 41 +- .../tensor_rt/tensor_rt_inference.hpp | 5 +- .../gxf_isaac_tensor_rt/package.xml | 47 ++ .../gxf_isaac_triton/CMakeLists.txt | 70 ++ .../lib/gxf_jetpack60/libgxf_isaac_triton.so | 3 + .../libgxf_isaac_triton.so | 3 + .../nvds/include/infer_custom_process.h | 2 +- .../nvds/include/infer_datatypes.h | 111 ++- .../nvds/include/infer_defines.h | 2 +- .../nvds/include/infer_icontext.h | 36 +- .../nvds/include/infer_ioptions.h | 2 +- .../nvds/include/infer_options.h | 2 +- .../nvds/include/infer_post_datatypes.h | 2 +- .../nvds/include/nvdsinfer.h | 7 +- .../nvds/include/nvdsinferserver_common.proto | 8 +- .../nvds/include/nvdsinferserver_config.proto | 12 +- .../nvds/include/nvdsinferserver_plugin.proto | 9 +- .../nvds/lib/gxf_jetpack60/libnvbuf_fdmap.so | 3 + .../nvds/lib/gxf_jetpack60/libnvbufsurface.so | 3 + .../gxf_jetpack60/libnvbufsurftransform.so | 3 + .../lib/gxf_jetpack60/libnvds_infer_server.so | 3 + .../lib/gxf_jetpack60/libnvds_inferlogger.so | 3 + .../lib/gxf_jetpack60/libnvds_inferutils.so | 3 + .../gxf_x86_64_cuda_12_2/libnvbuf_fdmap.so | 3 + .../gxf_x86_64_cuda_12_2/libnvbufsurface.so | 3 + .../libnvbufsurftransform.so | 3 + .../libnvds_infer_server.so | 3 + .../libnvds_inferlogger.so | 3 + .../libnvds_inferutils.so | 3 + .../gxf_isaac_triton/package.xml | 43 + isaac_ros_tensor_proc/CMakeLists.txt | 105 +++ .../config/interleaved_to_planar_node.yaml | 122 +++ ...e_injector_rule_interleaved_to_planar.yaml | 24 + .../namespace_injector_rule_normalizer.yaml | 24 + .../namespace_injector_rule_reshaper.yaml | 24 + .../config/normalizer_node.yaml | 124 +++ .../config/reshape_node.yaml | 127 +++ .../image_tensor_normalize_node.hpp | 79 ++ .../image_to_tensor_node.hpp | 72 ++ .../interleaved_to_planar_node.hpp | 51 ++ .../isaac_ros_tensor_proc/normalize_node.hpp | 54 ++ .../isaac_ros_tensor_proc/reshape_node.hpp | 51 ++ isaac_ros_tensor_proc/package.xml | 54 ++ .../src/image_tensor_normalize_node.cpp | 232 ++++++ .../src/image_to_tensor_node.cpp | 188 +++++ .../src/interleaved_to_planar_node.cpp | 151 ++++ isaac_ros_tensor_proc/src/normalize_node.cpp | 215 +++++ isaac_ros_tensor_proc/src/reshape_node.cpp | 183 ++++ .../isaac_ros_image_tensor_normalize_test.py | 146 ++++ .../test/isaac_ros_image_to_tensor.py | 126 +++ .../isaac_ros_image_to_tensor_no_scale.py | 126 +++ ...saac_ros_image_to_tensor_no_scale_float.py | 125 +++ .../isaac_ros_interleaved_to_planar_test.py | 146 ++++ .../test/isaac_ros_normalize_test.py | 136 +++ .../test/isaac_ros_reshape_test.py | 148 ++++ .../pose_estimation_0/camera_info.json | 51 ++ .../test_cases/pose_estimation_0/image.jpg | 3 + .../test_cases/pose_estimation_0/image.json | 4 + isaac_ros_tensor_rt/CMakeLists.txt | 25 +- .../config/isaac_ros_tensor_rt.yaml | 58 ++ .../config/tensor_rt_inference.yaml | 6 - isaac_ros_tensor_rt/gxf/AMENT_IGNORE | 0 .../isaac_ros_tensor_rt/tensor_rt_node.hpp | 2 +- .../launch/isaac_ros_tensor_rt.launch.py | 2 +- isaac_ros_tensor_rt/package.xml | 7 +- isaac_ros_tensor_rt/src/tensor_rt_node.cpp | 8 +- .../test/isaac_ros_tensor_rt_test.py | 2 +- .../test/tensor_rt_node_test.cpp | 49 ++ isaac_ros_triton/CMakeLists.txt | 23 +- isaac_ros_triton/config/triton_node.yaml | 7 +- isaac_ros_triton/gxf/AMENT_IGNORE | 0 isaac_ros_triton/gxf/triton/CMakeLists.txt | 115 --- .../extensions/triton/triton_options.hpp | 39 - .../extensions/triton/triton_server.cpp | 120 --- .../extensions/triton/triton_server.hpp | 159 ---- .../inferencers/triton_inferencer_impl.cpp | 781 ------------------ .../inferencers/triton_inferencer_impl.hpp | 291 ------- .../triton_inferencer_interface.hpp | 91 -- .../nvds/lib/gxf_jetpack502/libnvbuf_fdmap.so | 3 - .../lib/gxf_jetpack502/libnvbufsurface.so | 3 - .../gxf_jetpack502/libnvbufsurftransform.so | 3 - .../gxf_jetpack502/libnvds_infer_server.so | 3 - .../lib/gxf_jetpack502/libnvds_inferlogger.so | 3 - .../lib/gxf_jetpack502/libnvds_inferutils.so | 3 - .../gxf_x86_64_cuda_11_8/libnvbuf_fdmap.so | 3 - .../gxf_x86_64_cuda_11_8/libnvbufsurface.so | 3 - .../libnvbufsurftransform.so | 3 - .../libnvds_infer_server.so | 3 - .../libnvds_inferlogger.so | 3 - .../libnvds_inferutils.so | 3 - isaac_ros_triton/gxf/triton/triton_ext.cpp | 70 -- .../gxf/triton/triton_inference_request.cpp | 102 --- .../gxf/triton/triton_inference_request.hpp | 102 --- .../gxf/triton/triton_inference_response.cpp | 135 --- .../gxf/triton/triton_inference_response.hpp | 100 --- .../gxf/triton/triton_scheduling_terms.cpp | 49 -- .../gxf/triton/triton_scheduling_terms.hpp | 90 -- .../isaac_ros_triton_node/triton_node.hpp | 2 +- .../launch/isaac_ros_triton.launch.py | 2 +- isaac_ros_triton/package.xml | 6 +- isaac_ros_triton/src/triton_node.cpp | 4 +- .../test/isaac_ros_triton_test_onnx.py | 2 +- .../test/isaac_ros_triton_test_tf.py | 2 +- 116 files changed, 3924 insertions(+), 2529 deletions(-) create mode 100644 isaac_ros_dnn_image_encoder/launch/dnn_image_encoder.launch.py create mode 100644 isaac_ros_dnn_image_encoder/test/test_cases/pose_estimation_0/camera_info.json rename {isaac_ros_tensor_rt/gxf/tensor_rt => isaac_ros_gxf_extensions/gxf_isaac_tensor_rt}/CMakeLists.txt (50%) rename {isaac_ros_tensor_rt/gxf => isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions}/tensor_rt/tensor_rt_extension.cpp (86%) rename {isaac_ros_tensor_rt/gxf => isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions}/tensor_rt/tensor_rt_inference.cpp (97%) rename {isaac_ros_tensor_rt/gxf => isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions}/tensor_rt/tensor_rt_inference.hpp (96%) create mode 100644 isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/package.xml create mode 100644 isaac_ros_gxf_extensions/gxf_isaac_triton/CMakeLists.txt create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_jetpack60/libgxf_isaac_triton.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_x86_64_cuda_12_2/libgxf_isaac_triton.so rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_custom_process.h (98%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_datatypes.h (64%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_defines.h (98%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_icontext.h (84%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_ioptions.h (98%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_options.h (99%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/infer_post_datatypes.h (98%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/nvdsinfer.h (97%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/nvdsinferserver_common.proto (96%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/nvdsinferserver_config.proto (90%) rename {isaac_ros_triton/gxf/triton => isaac_ros_gxf_extensions/gxf_isaac_triton}/nvds/include/nvdsinferserver_plugin.proto (94%) create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbuf_fdmap.so create mode 100644 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurface.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurftransform.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_infer_server.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferlogger.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferutils.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbuf_fdmap.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurface.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurftransform.so create mode 100644 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_infer_server.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferlogger.so create mode 100755 isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferutils.so create mode 100644 isaac_ros_gxf_extensions/gxf_isaac_triton/package.xml create mode 100644 isaac_ros_tensor_proc/CMakeLists.txt create mode 100644 isaac_ros_tensor_proc/config/interleaved_to_planar_node.yaml create mode 100644 isaac_ros_tensor_proc/config/namespace_injector_rule_interleaved_to_planar.yaml create mode 100644 isaac_ros_tensor_proc/config/namespace_injector_rule_normalizer.yaml create mode 100644 isaac_ros_tensor_proc/config/namespace_injector_rule_reshaper.yaml create mode 100644 isaac_ros_tensor_proc/config/normalizer_node.yaml create mode 100644 isaac_ros_tensor_proc/config/reshape_node.yaml create mode 100644 isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_tensor_normalize_node.hpp create mode 100644 isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_to_tensor_node.hpp create mode 100644 isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/interleaved_to_planar_node.hpp create mode 100644 isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/normalize_node.hpp create mode 100644 isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/reshape_node.hpp create mode 100644 isaac_ros_tensor_proc/package.xml create mode 100644 isaac_ros_tensor_proc/src/image_tensor_normalize_node.cpp create mode 100644 isaac_ros_tensor_proc/src/image_to_tensor_node.cpp create mode 100644 isaac_ros_tensor_proc/src/interleaved_to_planar_node.cpp create mode 100644 isaac_ros_tensor_proc/src/normalize_node.cpp create mode 100644 isaac_ros_tensor_proc/src/reshape_node.cpp create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_image_tensor_normalize_test.py create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor.py create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale.py create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale_float.py create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_interleaved_to_planar_test.py create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_normalize_test.py create mode 100644 isaac_ros_tensor_proc/test/isaac_ros_reshape_test.py create mode 100644 isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/camera_info.json create mode 100644 isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.jpg create mode 100644 isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.json create mode 100644 isaac_ros_tensor_rt/config/isaac_ros_tensor_rt.yaml delete mode 100644 isaac_ros_tensor_rt/gxf/AMENT_IGNORE create mode 100644 isaac_ros_tensor_rt/test/tensor_rt_node_test.cpp delete mode 100644 isaac_ros_triton/gxf/AMENT_IGNORE delete mode 100644 isaac_ros_triton/gxf/triton/CMakeLists.txt delete mode 100644 isaac_ros_triton/gxf/triton/extensions/triton/triton_options.hpp delete mode 100644 isaac_ros_triton/gxf/triton/extensions/triton/triton_server.cpp delete mode 100644 isaac_ros_triton/gxf/triton/extensions/triton/triton_server.hpp delete mode 100644 isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.cpp delete mode 100644 isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.hpp delete mode 100644 isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_interface.hpp delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbuf_fdmap.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurface.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurftransform.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_infer_server.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferlogger.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferutils.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbuf_fdmap.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurface.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurftransform.so delete mode 100644 isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_infer_server.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferlogger.so delete mode 100755 isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferutils.so delete mode 100644 isaac_ros_triton/gxf/triton/triton_ext.cpp delete mode 100644 isaac_ros_triton/gxf/triton/triton_inference_request.cpp delete mode 100644 isaac_ros_triton/gxf/triton/triton_inference_request.hpp delete mode 100644 isaac_ros_triton/gxf/triton/triton_inference_response.cpp delete mode 100644 isaac_ros_triton/gxf/triton/triton_inference_response.hpp delete mode 100644 isaac_ros_triton/gxf/triton/triton_scheduling_terms.cpp delete mode 100644 isaac_ros_triton/gxf/triton/triton_scheduling_terms.hpp diff --git a/README.md b/README.md index d95bc08..0a468b9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Isaac ROS DNN Inference -Hardware-accelerated DNN model inference ROS 2 packages using NVIDIA Triton/TensorRT for both Jetson and x86_64 with CUDA-capable GPU. +NVIDIA-accelerated DNN model inference ROS 2 packages using NVIDIA Triton/TensorRT for both Jetson and x86_64 with CUDA-capable GPU.
bounding box for people detection segementation mask for people detection
@@ -70,13 +70,13 @@ This package is powered by [NVIDIA Isaac Transport for ROS (NITROS)](https://dev ## Performance -| Sample Graph

| Input Size

| AGX Orin

| Orin NX

| Orin Nano 8GB

| x86_64 w/ RTX 4060 Ti

| -|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [TensorRT Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts/isaac_ros_tensor_rt_dope_node.py)


DOPE

| VGA



| [48.2 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-agx_orin.json)


22 ms

| [18.5 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-orin_nx.json)


56 ms

| [13.0 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-orin_nano.json)


81 ms

| [103 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-nuc_4060ti.json)


11 ms

| -| [Triton Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts/isaac_ros_triton_dope_node.py)


DOPE

| VGA



| [47.8 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-agx_orin.json)


23 ms

| [20.4 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-orin_nx.json)


540 ms

| [14.5 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-orin_nano.json)


790 ms

| [99.0 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-nuc_4060ti.json)


10 ms

| -| [TensorRT Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts/isaac_ros_tensor_rt_ps_node.py)


PeopleSemSegNet

| 544p



| [600 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-agx_orin.json)


3.4 ms

| [324 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-orin_nx.json)


5.6 ms

| [215 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-orin_nano.json)


6.4 ms

| [721 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-nuc_4060ti.json)


2.2 ms

| -| [Triton Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts/isaac_ros_triton_ps_node.py)


PeopleSemSegNet

| 544p



| [311 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-agx_orin.json)


4.0 ms

| [194 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-orin_nx.json)


5.7 ms

| –



| [682 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-nuc_4060ti.json)


2.0 ms

| -| [DNN Image Encoder Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/scripts/isaac_ros_dnn_image_encoder_node.py)



| VGA



| [1120 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-agx_orin.json)


1.6 ms

| [1070 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-orin_nx.json)


2.3 ms

| –



| [3020 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-nuc_4060ti.json)


0.66 ms

| +| Sample Graph

| Input Size

| AGX Orin

| Orin NX

| Orin Nano 8GB

| x86_64 w/ RTX 4060 Ti

| x86_64 w/ RTX 4090

| +|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [TensorRT Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/benchmarks/isaac_ros_tensor_rt_benchmark/scripts/isaac_ros_tensor_rt_dope_node.py)


DOPE

| VGA



| [48.1 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-agx_orin.json)


24 ms @ 30Hz

| [17.9 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-orin_nx.json)


56 ms @ 30Hz

| [13.1 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-orin_nano.json)


82 ms @ 30Hz

| [98.3 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-nuc_4060ti.json)


13 ms @ 30Hz

| [296 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_dope_node-x86_4090.json)


5.1 ms @ 30Hz

| +| [Triton Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/benchmarks/isaac_ros_triton_benchmark/scripts/isaac_ros_triton_dope_node.py)


DOPE

| VGA



| [47.2 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-agx_orin.json)


23 ms @ 30Hz

| [20.4 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-orin_nx.json)


540 ms @ 30Hz

| [14.4 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-orin_nano.json)


790 ms @ 30Hz

| [94.2 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-nuc_4060ti.json)


12 ms @ 30Hz

| [254 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_dope_node-x86_4090.json)


4.6 ms @ 30Hz

| +| [TensorRT Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/benchmarks/isaac_ros_tensor_rt_benchmark/scripts/isaac_ros_tensor_rt_ps_node.py)


PeopleSemSegNet

| 544p



| [460 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-agx_orin.json)


4.1 ms @ 30Hz

| [348 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-orin_nx.json)


6.1 ms @ 30Hz

| [238 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-orin_nano.json)


7.0 ms @ 30Hz

| [685 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-nuc_4060ti.json)


2.9 ms @ 30Hz

| [675 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_tensor_rt_ps_node-x86_4090.json)


3.0 ms @ 30Hz

| +| [Triton Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/benchmarks/isaac_ros_triton_benchmark/scripts/isaac_ros_triton_ps_node.py)


PeopleSemSegNet

| 544p



| [304 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-agx_orin.json)


4.8 ms @ 30Hz

| [206 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-orin_nx.json)


6.5 ms @ 30Hz

| –



| [677 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-nuc_4060ti.json)


2.2 ms @ 30Hz

| [619 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_triton_ps_node-x86_4090.json)


1.9 ms @ 30Hz

| +| [DNN Image Encoder Node](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/benchmarks/isaac_ros_dnn_image_encoder_benchmark/scripts/isaac_ros_dnn_image_encoder_node.py)



| VGA



| [522 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-agx_orin.json)


12 ms @ 30Hz

| [330 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-orin_nx.json)


12 ms @ 30Hz

| –



| [811 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-nuc_4060ti.json)


6.6 ms @ 30Hz

| [822 fps](https://github.com/NVIDIA-ISAAC-ROS/isaac_ros_benchmark/blob/main/results/isaac_ros_dnn_image_encoder_node-x86_4090.json)


6.4 ms @ 30Hz

| --- @@ -89,7 +89,10 @@ Please visit the [Isaac ROS Documentation](https://nvidia-isaac-ros.github.io/re ## Packages * [`isaac_ros_dnn_image_encoder`](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_dnn_image_encoder/index.html) + * [Migration Guide](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_dnn_image_encoder/index.html#migration-guide) * [API](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_dnn_image_encoder/index.html#api) +* [`isaac_ros_tensor_proc`](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_tensor_proc/index.html) + * [API](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_tensor_proc/index.html#api) * [`isaac_ros_tensor_rt`](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_tensor_rt/index.html) * [Quickstart](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_tensor_rt/index.html#quickstart) * [Troubleshooting](https://nvidia-isaac-ros.github.io/repositories_and_packages/isaac_ros_dnn_inference/isaac_ros_tensor_rt/index.html#troubleshooting) @@ -101,4 +104,4 @@ Please visit the [Isaac ROS Documentation](https://nvidia-isaac-ros.github.io/re ## Latest -Update 2023-10-18: Updated for Isaac ROS 2.0.0. +Update 2024-05-30: Update to be compatible with JetPack 6.0 diff --git a/isaac_ros_dnn_image_encoder/CMakeLists.txt b/isaac_ros_dnn_image_encoder/CMakeLists.txt index 77dc326..3b42035 100644 --- a/isaac_ros_dnn_image_encoder/CMakeLists.txt +++ b/isaac_ros_dnn_image_encoder/CMakeLists.txt @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ # SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.22.1) -project(isaac_ros_dnn_image_encoder LANGUAGES C CXX) +project(isaac_ros_dnn_image_encoder LANGUAGES C CXX CUDA) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) @@ -29,6 +29,10 @@ ament_auto_find_build_dependencies() ament_auto_add_library(dnn_image_encoder_node SHARED src/dnn_image_encoder_node.cpp) rclcpp_components_register_nodes(dnn_image_encoder_node "nvidia::isaac_ros::dnn_inference::DnnImageEncoderNode") set(node_plugins "${node_plugins}nvidia::isaac_ros::dnn_inference::DnnImageEncoderNode;$\n") +set_target_properties(dnn_image_encoder_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) @@ -46,4 +50,4 @@ if(BUILD_TESTING) add_launch_test(test/isaac_ros_dnn_image_encoder_image_norm_test.py) endif() -ament_auto_package(INSTALL_TO_SHARE config) +ament_auto_package(INSTALL_TO_SHARE config launch) diff --git a/isaac_ros_dnn_image_encoder/config/dnn_image_encoder_node.yaml b/isaac_ros_dnn_image_encoder/config/dnn_image_encoder_node.yaml index 9f598c6..c3dec03 100644 --- a/isaac_ros_dnn_image_encoder/config/dnn_image_encoder_node.yaml +++ b/isaac_ros_dnn_image_encoder/config/dnn_image_encoder_node.yaml @@ -62,7 +62,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 1 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -82,7 +81,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -150,7 +148,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -159,7 +156,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: bbox_receiver @@ -199,7 +195,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -235,7 +230,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -272,7 +266,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -307,7 +300,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: data_receiver @@ -316,7 +308,6 @@ components: type: nvidia::gxf::DoubleBufferTransmitter parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::DownstreamReceptiveSchedulingTerm parameters: transmitter: data_transmitter @@ -348,7 +339,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 5 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: signal @@ -415,11 +405,10 @@ components: target: sink/signal --- components: -- type: nvidia::gxf::MultiThreadScheduler +- type: nvidia::gxf::EventBasedScheduler parameters: clock: clock stop_on_deadlock: false - check_recession_period_ms: 0.05 worker_thread_number: 2 - name: clock type: nvidia::gxf::RealtimeClock diff --git a/isaac_ros_dnn_image_encoder/include/isaac_ros_dnn_image_encoder/dnn_image_encoder_node.hpp b/isaac_ros_dnn_image_encoder/include/isaac_ros_dnn_image_encoder/dnn_image_encoder_node.hpp index 7a3b5bc..2aa5bdb 100644 --- a/isaac_ros_dnn_image_encoder/include/isaac_ros_dnn_image_encoder/dnn_image_encoder_node.hpp +++ b/isaac_ros_dnn_image_encoder/include/isaac_ros_dnn_image_encoder/dnn_image_encoder_node.hpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_dnn_image_encoder/launch/dnn_image_encoder.launch.py b/isaac_ros_dnn_image_encoder/launch/dnn_image_encoder.launch.py new file mode 100644 index 0000000..bb8690b --- /dev/null +++ b/isaac_ros_dnn_image_encoder/launch/dnn_image_encoder.launch.py @@ -0,0 +1,288 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import ast + +from launch import LaunchDescription +from launch.actions import (DeclareLaunchArgument, GroupAction, OpaqueFunction) +from launch.conditions import UnlessCondition +from launch.substitutions import LaunchConfiguration +from launch_ros.actions import LoadComposableNodes, Node, PushRosNamespace +from launch_ros.descriptions import ComposableNode + + +def launch_setup(context, *args, **kwargs): + input_image_width = int( + context.perform_substitution(LaunchConfiguration('input_image_width')) + ) + input_image_height = int( + context.perform_substitution(LaunchConfiguration('input_image_height')) + ) + + network_image_width = int( + context.perform_substitution(LaunchConfiguration('network_image_width')) + ) + network_image_height = int( + context.perform_substitution(LaunchConfiguration('network_image_height')) + ) + enable_padding = ast.literal_eval( + context.perform_substitution(LaunchConfiguration('enable_padding')) + ) + + keep_aspect_ratio = LaunchConfiguration('keep_aspect_ratio') + crop_mode = LaunchConfiguration('crop_mode') + encoding_desired = LaunchConfiguration('encoding_desired') + final_tensor_name = LaunchConfiguration('final_tensor_name') + + image_mean = LaunchConfiguration('image_mean') + image_stddev = LaunchConfiguration('image_stddev') + num_blocks = LaunchConfiguration('num_blocks') + + image_input_topic = LaunchConfiguration('image_input_topic', default='image') + camera_info_input_topic = LaunchConfiguration('camera_info_input_topic', default='camera_info') + tensor_output_topic = LaunchConfiguration('tensor_output_topic', default='encoded_tensor') + + attach_to_shared_component_container_arg = LaunchConfiguration( + 'attach_to_shared_component_container', default=False + ) + component_container_name_arg = LaunchConfiguration( + 'component_container_name', default='dnn_image_encoder_container' + ) + dnn_image_encoder_namespace = LaunchConfiguration('dnn_image_encoder_namespace') + + resize_factor = 1.0 + if not enable_padding: + width_scalar = input_image_width / network_image_width + height_scalar = input_image_height / network_image_height + if width_scalar != height_scalar: + resize_factor = min(width_scalar, height_scalar) + + resize_image_width = int(network_image_width * resize_factor) + resize_image_height = int(network_image_height * resize_factor) + + # If we do not attach to a shared component container we have to create our own container. + dnn_image_encoder_container = Node( + name=component_container_name_arg, + package='rclcpp_components', + executable='component_container_mt', + output='screen', + condition=UnlessCondition(attach_to_shared_component_container_arg), + ) + + load_composable_nodes = LoadComposableNodes( + target_container=component_container_name_arg, + composable_node_descriptions=[ + ComposableNode( + name='resize_node', + package='isaac_ros_image_proc', + plugin='nvidia::isaac_ros::image_proc::ResizeNode', + parameters=[ + { + 'output_width': resize_image_width, + 'output_height': resize_image_height, + 'num_blocks': num_blocks, + 'keep_aspect_ratio': keep_aspect_ratio, + 'encoding_desired': '', + } + ], + remappings=[ + ('image', image_input_topic), + ('camera_info', camera_info_input_topic), + ], + ), + ComposableNode( + name='crop_node', + package='isaac_ros_image_proc', + plugin='nvidia::isaac_ros::image_proc::CropNode', + parameters=[ + { + 'input_width': resize_image_width, + 'input_height': resize_image_height, + 'crop_width': network_image_width, + 'crop_height': network_image_height, + 'num_blocks': num_blocks, + 'crop_mode': crop_mode, + 'roi_top_left_x': int((resize_image_width - network_image_width) / 2.0), + 'roi_top_left_y': int((resize_image_height - network_image_height) / 2.0), + } + ], + remappings=[ + ('image', 'resize/image'), + ('camera_info', 'resize/camera_info'), + ], + ), + ComposableNode( + name='image_format_converter_node', + package='isaac_ros_image_proc', + plugin='nvidia::isaac_ros::image_proc::ImageFormatConverterNode', + parameters=[ + { + 'image_width': network_image_width, + 'image_height': network_image_height, + 'encoding_desired': encoding_desired, + } + ], + remappings=[ + ('image_raw', 'crop/image'), + ('image', 'converted/image'), + ], + ), + ComposableNode( + name='image_to_tensor', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ImageToTensorNode', + parameters=[ + { + 'scale': True, + 'tensor_name': 'image', + } + ], + remappings=[ + ('image', 'converted/image'), + ('tensor', 'image_tensor'), + ], + ), + ComposableNode( + name='normalize_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ImageTensorNormalizeNode', + parameters=[ + { + 'mean': image_mean, + 'stddev': image_stddev, + 'input_tensor_name': 'image', + 'output_tensor_name': 'image' + } + ], + remappings=[ + ('tensor', 'image_tensor'), + ], + ), + ComposableNode( + name='interleaved_to_planar_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::InterleavedToPlanarNode', + parameters=[ + { + 'input_tensor_shape': [network_image_height, network_image_width, 3], + 'num_blocks': num_blocks, + } + ], + remappings=[ + ('interleaved_tensor', 'normalized_tensor'), + ], + ), + ComposableNode( + name='reshape_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ReshapeNode', + parameters=[ + { + 'output_tensor_name': final_tensor_name, + 'input_tensor_shape': [3, network_image_height, network_image_width], + 'output_tensor_shape': [1, 3, network_image_height, network_image_width], + 'num_blocks': num_blocks, + } + ], + remappings=[ + ('tensor', 'planar_tensor'), + ('reshaped_tensor', tensor_output_topic), + ], + ), + ], + ) + + final_launch = GroupAction( + actions=[ + dnn_image_encoder_container, + PushRosNamespace(dnn_image_encoder_namespace), + load_composable_nodes, + ], + ) + return [final_launch] + + +def generate_launch_description(): + launch_args = [ + DeclareLaunchArgument( + 'input_image_width', + default_value='0', + description='The input image width', + ), + DeclareLaunchArgument( + 'input_image_height', + default_value='0', + description='The input image height', + ), + DeclareLaunchArgument( + 'network_image_width', + default_value='0', + description='The network image width', + ), + DeclareLaunchArgument( + 'network_image_height', + default_value='0', + description='The network image height', + ), + DeclareLaunchArgument( + 'image_mean', + default_value='[0.5, 0.5, 0.5]', + description='The mean for image normalization', + ), + DeclareLaunchArgument( + 'image_stddev', + default_value='[0.5, 0.5, 0.5]', + description='The standard deviation for image normalization', + ), + DeclareLaunchArgument( + 'enable_padding', + default_value='True', + description='Whether to enable padding or not', + ), + DeclareLaunchArgument( + 'num_blocks', + default_value='40', + description='The number of preallocated memory blocks', + ), + DeclareLaunchArgument( + 'keep_aspect_ratio', + default_value='True', + description='Whether to maintain the aspect ratio or not while resizing' + ), + DeclareLaunchArgument( + 'crop_mode', + default_value='CENTER', + description='The crop mode to crop the image using', + ), + DeclareLaunchArgument( + 'encoding_desired', + default_value='rgb8', + description='The desired image format encoding', + ), + DeclareLaunchArgument( + 'final_tensor_name', + default_value='input_tensor', + description='The tensor name of the output of image encoder', + ), + DeclareLaunchArgument( + 'dnn_image_encoder_namespace', + default_value='dnn_image_encoder', + description='The namespace to put the DNN image encoder under', + ), + ] + + return LaunchDescription(launch_args + [OpaqueFunction(function=launch_setup)]) diff --git a/isaac_ros_dnn_image_encoder/package.xml b/isaac_ros_dnn_image_encoder/package.xml index fec079e..716fb98 100644 --- a/isaac_ros_dnn_image_encoder/package.xml +++ b/isaac_ros_dnn_image_encoder/package.xml @@ -21,9 +21,9 @@ SPDX-License-Identifier: Apache-2.0 isaac_ros_dnn_image_encoder - 2.1.0 + 3.0.0 Encoder for preprocessing images into tensors for deep learning inference - Hemal Shah + Isaac ROS Maintainers Apache-2.0 https://developer.nvidia.com/isaac-ros-gems/ Ethan Yu @@ -36,10 +36,15 @@ SPDX-License-Identifier: Apache-2.0 isaac_ros_nitros isaac_ros_nitros_image_type isaac_ros_nitros_tensor_list_type + isaac_ros_managed_nitros + isaac_ros_tensor_proc isaac_ros_common isaac_ros_gxf + gxf_isaac_message_compositor + gxf_isaac_tensorops + ament_lint_auto ament_lint_common isaac_ros_test diff --git a/isaac_ros_dnn_image_encoder/src/dnn_image_encoder_node.cpp b/isaac_ros_dnn_image_encoder/src/dnn_image_encoder_node.cpp index 274b665..558ceff 100644 --- a/isaac_ros_dnn_image_encoder/src/dnn_image_encoder_node.cpp +++ b/isaac_ros_dnn_image_encoder/src/dnn_image_encoder_node.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -62,8 +62,8 @@ const std::vector> EXTENSIONS = { {"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"}, {"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"}, {"isaac_ros_gxf", "gxf/lib/serialization/libgxf_serialization.so"}, - {"isaac_ros_image_proc", "gxf/lib/image_proc/libgxf_tensorops.so"}, - {"isaac_ros_gxf", "gxf/lib/libgxf_message_compositor.so"} + {"gxf_isaac_tensorops", "gxf/lib/libgxf_isaac_tensorops.so"}, + {"gxf_isaac_message_compositor", "gxf/lib/libgxf_isaac_message_compositor.so"} }; const std::vector PRESET_EXTENSION_SPEC_NAMES = { "isaac_ros_dnn_encoders", diff --git a/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_crop_test.py b/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_crop_test.py index 18b14ed..15691c6 100644 --- a/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_crop_test.py +++ b/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_crop_test.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2023-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,17 +21,18 @@ import struct import time +from ament_index_python.packages import get_package_share_directory from cv_bridge import CvBridge from isaac_ros_tensor_list_interfaces.msg import TensorList from isaac_ros_test import IsaacROSBaseTest -from launch_ros.actions import ComposableNodeContainer -from launch_ros.descriptions import ComposableNode +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource import numpy as np import pytest import rclpy -from sensor_msgs.msg import Image +from sensor_msgs.msg import CameraInfo, Image INPUT_IMAGE_WIDTH = 1920 @@ -45,33 +46,27 @@ @pytest.mark.rostest def generate_test_description(): - encoder_node = ComposableNode( - name='encoder', - package='isaac_ros_dnn_image_encoder', - plugin='nvidia::isaac_ros::dnn_inference::DnnImageEncoderNode', - namespace=IsaacROSDnnImageEncoderImageResizeNodeTest.generate_namespace(), - parameters=[{ - 'input_image_width': INPUT_IMAGE_WIDTH, - 'input_image_height': INPUT_IMAGE_HEIGHT, - 'network_image_width': NETWORK_IMAGE_WIDTH, - 'network_image_height': NETWORK_IMAGE_HEIGHT, - 'image_mean': list(IMAGE_MEAN), - 'image_stddev': list(IMAGE_STDDEV), - 'enable_padding': True - }], - remappings=[('encoded_tensor', 'tensors')]) + encoder_dir = get_package_share_directory('isaac_ros_dnn_image_encoder') + namespace = IsaacROSDnnImageEncoderImageResizeNodeTest.generate_namespace() + encoder_node_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + [os.path.join(encoder_dir, 'launch', 'dnn_image_encoder.launch.py')] + ), + launch_arguments={ + 'input_image_width': f'{INPUT_IMAGE_WIDTH}', + 'input_image_height': f'{INPUT_IMAGE_HEIGHT}', + 'network_image_width': f'{NETWORK_IMAGE_WIDTH}', + 'network_image_height': f'{NETWORK_IMAGE_HEIGHT}', + 'image_mean': str(list(IMAGE_MEAN)), + 'image_stddev': str(list(IMAGE_STDDEV)), + 'enable_padding': 'True', + 'tensor_output_topic': 'tensors', + 'dnn_image_encoder_namespace': namespace, + }.items(), + ) return IsaacROSDnnImageEncoderImageResizeNodeTest.generate_test_description([ - ComposableNodeContainer( - name='tensor_rt_container', - package='rclcpp_components', - executable='component_container_mt', - composable_node_descriptions=[encoder_node], - namespace=IsaacROSDnnImageEncoderImageResizeNodeTest.generate_namespace(), - output='screen', - arguments=['--ros-args', '--log-level', 'info', - '--log-level', 'isaac_ros_test.encoder:=debug'], - ) + encoder_node_launch, ]) @@ -83,11 +78,14 @@ def test_image_resize(self): TIMEOUT = 300 received_messages = {} - self.generate_namespace_lookup(['image', 'tensors']) + self.generate_namespace_lookup(['image', 'camera_info', 'tensors']) image_pub = self.node.create_publisher( Image, self.namespaces['image'], self.DEFAULT_QOS) + camera_info_pub = self.node.create_publisher( + CameraInfo, self.namespaces['camera_info'], self.DEFAULT_QOS) + subs = self.create_logging_subscribers( [('tensors', TensorList)], received_messages, accept_multiple_messages=False) @@ -125,11 +123,15 @@ def test_image_resize(self): image = CvBridge().cv2_to_imgmsg(cv_image) image.encoding = 'bgr8' + camera_info = CameraInfo() + camera_info.distortion_model = 'plumb_bob' + end_time = time.time() + TIMEOUT done = False while time.time() < end_time: image_pub.publish(image) + camera_info_pub.publish(camera_info) rclpy.spin_once(self.node, timeout_sec=(0.1)) if 'tensors' in received_messages: done = True diff --git a/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_norm_test.py b/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_norm_test.py index 9490431..17082a3 100644 --- a/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_norm_test.py +++ b/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_image_norm_test.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -21,17 +21,18 @@ import struct import time +from ament_index_python.packages import get_package_share_directory from cv_bridge import CvBridge from isaac_ros_tensor_list_interfaces.msg import TensorList from isaac_ros_test import IsaacROSBaseTest -from launch_ros.actions import ComposableNodeContainer -from launch_ros.descriptions import ComposableNode +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource import numpy as np import pytest import rclpy -from sensor_msgs.msg import Image +from sensor_msgs.msg import CameraInfo, Image DIMENSION_WIDTH = 100 @@ -40,32 +41,27 @@ @pytest.mark.rostest def generate_test_description(): - encoder_node = ComposableNode( - name='encoder', - package='isaac_ros_dnn_image_encoder', - plugin='nvidia::isaac_ros::dnn_inference::DnnImageEncoderNode', - namespace=IsaacROSDnnImageEncoderImageNormNodeTest.generate_namespace(), - parameters=[{ - 'input_image_width': DIMENSION_WIDTH, - 'input_image_height': DIMENSION_HEIGHT, - 'network_image_width': DIMENSION_WIDTH, - 'network_image_height': DIMENSION_HEIGHT, - 'image_mean': [0.5, 0.6, 0.25], - 'image_stddev': [0.25, 0.8, 0.5] - }], - remappings=[('encoded_tensor', 'tensors')]) + encoder_dir = get_package_share_directory('isaac_ros_dnn_image_encoder') + namespace = IsaacROSDnnImageEncoderImageNormNodeTest.generate_namespace() + encoder_node_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + [os.path.join(encoder_dir, 'launch', 'dnn_image_encoder.launch.py')] + ), + launch_arguments={ + 'input_image_width': f'{DIMENSION_WIDTH}', + 'input_image_height': f'{DIMENSION_HEIGHT}', + 'network_image_width': f'{DIMENSION_WIDTH}', + 'network_image_height': f'{DIMENSION_HEIGHT}', + 'image_mean': str([0.5, 0.6, 0.25]), + 'image_stddev': str([0.25, 0.8, 0.5]), + 'enable_padding': 'True', + 'tensor_output_topic': 'tensors', + 'dnn_image_encoder_namespace': namespace, + }.items(), + ) return IsaacROSDnnImageEncoderImageNormNodeTest.generate_test_description([ - ComposableNodeContainer( - name='tensor_rt_container', - package='rclcpp_components', - executable='component_container_mt', - composable_node_descriptions=[encoder_node], - namespace=IsaacROSDnnImageEncoderImageNormNodeTest.generate_namespace(), - output='screen', - arguments=['--ros-args', '--log-level', 'info', - '--log-level', 'isaac_ros_test.encoder:=debug'], - ) + encoder_node_launch ]) @@ -93,11 +89,14 @@ def test_image_normalization(self): GREEN_EXPECTED_VAL = 0.5 BLUE_EXPECTED_VAL = 1.5 - self.generate_namespace_lookup(['image', 'tensors']) + self.generate_namespace_lookup(['image', 'camera_info', 'tensors']) image_pub = self.node.create_publisher( Image, self.namespaces['image'], self.DEFAULT_QOS) + camera_info_pub = self.node.create_publisher( + CameraInfo, self.namespaces['camera_info'], self.DEFAULT_QOS) + subs = self.create_logging_subscribers( [('tensors', TensorList)], received_messages) @@ -108,11 +107,15 @@ def test_image_normalization(self): image = CvBridge().cv2_to_imgmsg(cv_image) image.encoding = 'bgr8' + camera_info = CameraInfo() + camera_info.distortion_model = 'plumb_bob' + end_time = time.time() + TIMEOUT done = False while time.time() < end_time: image_pub.publish(image) + camera_info_pub.publish(camera_info) rclpy.spin_once(self.node, timeout_sec=(0.1)) if 'tensors' in received_messages: done = True diff --git a/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_test.py b/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_test.py index d60129f..8fde0f5 100644 --- a/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_test.py +++ b/isaac_ros_dnn_image_encoder/test/isaac_ros_dnn_image_encoder_test.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,46 +19,41 @@ import pathlib import time + +from ament_index_python.packages import get_package_share_directory import cv2 from cv_bridge import CvBridge from isaac_ros_tensor_list_interfaces.msg import TensorList from isaac_ros_test import IsaacROSBaseTest, JSONConversion -from launch_ros.actions import ComposableNodeContainer -from launch_ros.descriptions import ComposableNode +from launch.actions import IncludeLaunchDescription +from launch.launch_description_sources import PythonLaunchDescriptionSource import pytest import rclpy -from sensor_msgs.msg import Image +from sensor_msgs.msg import CameraInfo, Image @pytest.mark.rostest def generate_test_description(): - encoder_node = ComposableNode( - name='encoder', - package='isaac_ros_dnn_image_encoder', - plugin='nvidia::isaac_ros::dnn_inference::DnnImageEncoderNode', - namespace=IsaacROSDnnImageEncoderNodeTest.generate_namespace(), - parameters=[{ - 'input_image_width': 1920, - 'input_image_height': 1080, - 'network_image_width': 512, - 'network_image_height': 512, - 'enable_padding': True - }], - remappings=[('encoded_tensor', 'tensors')]) + encoder_dir = get_package_share_directory('isaac_ros_dnn_image_encoder') + encoder_node_launch = IncludeLaunchDescription( + PythonLaunchDescriptionSource( + [os.path.join(encoder_dir, 'launch', 'dnn_image_encoder.launch.py')] + ), + launch_arguments={ + 'input_image_width': '1920', + 'input_image_height': '1080', + 'network_image_width': '512', + 'network_image_height': '512', + 'enable_padding': 'True', + 'tensor_output_topic': 'tensors', + 'dnn_image_encoder_namespace': IsaacROSDnnImageEncoderNodeTest.generate_namespace(), + }.items(), + ) return IsaacROSDnnImageEncoderNodeTest.generate_test_description([ - ComposableNodeContainer( - name='tensor_rt_container', - package='rclcpp_components', - executable='component_container_mt', - composable_node_descriptions=[encoder_node], - namespace=IsaacROSDnnImageEncoderNodeTest.generate_namespace(), - output='screen', - arguments=['--ros-args', '--log-level', 'info', - '--log-level', 'isaac_ros_test.encoder:=debug'], - ) + encoder_node_launch, ]) @@ -76,11 +71,14 @@ def test_maintain_aspect_ratio(self): TIMEOUT = 300 received_messages = {} - self.generate_namespace_lookup(['image', 'tensors']) + self.generate_namespace_lookup(['image', 'camera_info', 'tensors']) image_pub = self.node.create_publisher( Image, self.namespaces['image'], self.DEFAULT_QOS) + camera_info_pub = self.node.create_publisher( + CameraInfo, self.namespaces['camera_info'], self.DEFAULT_QOS) + # The current DOPE decoder outputs TensorList subs = self.create_logging_subscribers( [('tensors', TensorList)], received_messages) @@ -88,14 +86,18 @@ def test_maintain_aspect_ratio(self): try: json_file = self.filepath / 'test_cases/pose_estimation_0/image.json' image = JSONConversion.load_image_from_json(json_file) + info_file = self.filepath / 'test_cases/pose_estimation_0/camera_info.json' + camera_info = JSONConversion.load_camera_info_from_json(info_file) timestamp = self.node.get_clock().now().to_msg() image.header.stamp = timestamp + camera_info.header = image.header end_time = time.time() + TIMEOUT done = False while time.time() < end_time: image_pub.publish(image) + camera_info_pub.publish(camera_info) rclpy.spin_once(self.node, timeout_sec=(0.1)) if 'tensors' in received_messages: done = True diff --git a/isaac_ros_dnn_image_encoder/test/test_cases/pose_estimation_0/camera_info.json b/isaac_ros_dnn_image_encoder/test/test_cases/pose_estimation_0/camera_info.json new file mode 100644 index 0000000..396a04b --- /dev/null +++ b/isaac_ros_dnn_image_encoder/test/test_cases/pose_estimation_0/camera_info.json @@ -0,0 +1,51 @@ +{ + "header": { + "frame_id": "tf_camera" + }, + "width": 852, + "height": 480, + "distortion_model": "plumb_bob", + "D": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "K": [ + 651.2994384765625, + 0.000000, + 298.3225504557292, + 0.0, + 651.2994384765625, + 392.1635182698568, + 0.0, + 0.0, + 1.0 + ], + "R": [ + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "P": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0 + ] +} diff --git a/isaac_ros_tensor_rt/gxf/tensor_rt/CMakeLists.txt b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/CMakeLists.txt similarity index 50% rename from isaac_ros_tensor_rt/gxf/tensor_rt/CMakeLists.txt rename to isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/CMakeLists.txt index 23edf30..1a3a807 100644 --- a/isaac_ros_tensor_rt/gxf/tensor_rt/CMakeLists.txt +++ b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/CMakeLists.txt @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2022-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,34 +15,43 @@ # # SPDX-License-Identifier: Apache-2.0 -project(gxf_tensor_rt LANGUAGES C CXX) +cmake_minimum_required(VERSION 3.22.1) +project(gxf_isaac_tensor_rt LANGUAGES C CXX) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-fPIC -w) endif() +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() + # Dependencies find_package(CUDAToolkit) -find_package(GXF ${ISAAC_ROS_GXF_VERSION} MODULE REQUIRED - COMPONENTS - core - cuda - std -) find_package(TENSORRT 8 MODULE REQUIRED) find_package(yaml-cpp) # TensorRT extension -add_library(gxf_tensor_rt SHARED - tensor_rt_extension.cpp - tensor_rt_inference.cpp - tensor_rt_inference.hpp +ament_auto_add_library(${PROJECT_NAME} SHARED + gxf/extensions/tensor_rt/tensor_rt_extension.cpp + gxf/extensions/tensor_rt/tensor_rt_inference.cpp + gxf/extensions/tensor_rt/tensor_rt_inference.hpp +) + +target_link_libraries(${PROJECT_NAME} + CUDA::cudart + TENSORRT::nvonnxparser + yaml-cpp ) -target_link_libraries(gxf_tensor_rt - PUBLIC - CUDA::cudart - GXF::std - GXF::cuda - TENSORRT::nvonnxparser - yaml-cpp + +target_include_directories(${PROJECT_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/gxf") + +set_target_properties(${PROJECT_NAME} PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE ) + +# Install the binary file +install(TARGETS ${PROJECT_NAME} DESTINATION share/${PROJECT_NAME}/gxf/lib) + +ament_auto_package(INSTALL_TO_SHARE) \ No newline at end of file diff --git a/isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_extension.cpp b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_extension.cpp similarity index 86% rename from isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_extension.cpp rename to isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_extension.cpp index 3a980d5..898f8e2 100644 --- a/isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_extension.cpp +++ b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_extension.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include "tensor_rt_inference.hpp" +#include "extensions/tensor_rt/tensor_rt_inference.hpp" #include "gxf/core/gxf.h" #include "gxf/std/extension_factory_helper.hpp" @@ -25,7 +25,7 @@ extern "C" { GXF_EXT_FACTORY_BEGIN() GXF_EXT_FACTORY_SET_INFO(0xd43f23e4b9bf11eb, 0x9d182b7be630552b, "TensorRTExtension", "TensorRT", - "Nvidia", "2.2.0", "LICENSE"); + "Nvidia", "2.5.0", "LICENSE"); GXF_EXT_FACTORY_ADD(0x06a7f0e0b9c011eb, 0x8cd623c9c2070107, nvidia::gxf::TensorRtInference, nvidia::gxf::Codelet, diff --git a/isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_inference.cpp b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_inference.cpp similarity index 97% rename from isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_inference.cpp rename to isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_inference.cpp index 743dcc6..299fce8 100644 --- a/isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_inference.cpp +++ b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_inference.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 -#include "tensor_rt_inference.hpp" +#include "extensions/tensor_rt/tensor_rt_inference.hpp" #include #include @@ -107,10 +107,13 @@ gxf::Expected NvInferDatatypeToTensorElementType(nvinfer1::D case nvinfer1::DataType::kINT32: { return gxf::PrimitiveType::kInt32; } + case nvinfer1::DataType::kUINT8: { + return gxf::PrimitiveType::kUnsigned8; + } // case nvinfer1::DataType::kBOOL: case nvinfer1::DataType::kHALF: default: { - GXF_LOG_ERROR("Unsupported DataType %d", data_type); + GXF_LOG_ERROR("Unsupported DataType %d", static_cast(data_type)); return gxf::Unexpected{GXF_FAILURE}; } } @@ -296,11 +299,15 @@ gxf_result_t TensorRtInference::start() { } // Creates inference runtime for the plan - NvInferHandle infer_runtime(nvinfer1::createInferRuntime(cuda_logger_)); + infer_runtime_.reset(nvinfer1::createInferRuntime(cuda_logger_)); + if (!infer_runtime_) { + GXF_LOG_ERROR("Unable to create runtime."); + return GXF_FAILURE; + } // Deserialize the CUDA engine if (verbose_.get()) { GXF_LOG_INFO("Creating inference runtime."); } - cuda_engine_.reset(infer_runtime->deserializeCudaEngine(plan.data(), plan.size(), NULL)); + cuda_engine_.reset(infer_runtime_->deserializeCudaEngine(plan.data(), plan.size(), NULL)); // Debug spews if (verbose_.get()) { @@ -580,7 +587,8 @@ gxf_result_t TensorRtInference::tick() { // Checks input tensor element type if (maybe_tensor.value()->element_type() != binding_info.element_type) { GXF_LOG_ERROR("Mismatching tensor element type required %d vs provided %d", - binding_info.element_type, maybe_tensor.value()->element_type()); + static_cast(binding_info.element_type), + static_cast(maybe_tensor.value()->element_type())); return GXF_FAILURE; } @@ -594,18 +602,18 @@ gxf_result_t TensorRtInference::tick() { if (shape_rank_matched >= shape_rank || binding_rank_matched >= binding_info.rank) { break; } - if (shape.dimension(shape_rank_matched) == 1) { + if (binding_info.dimensions[binding_rank_matched] == -1) { + // Matches dimension + dims.d[binding_rank_matched] = shape.dimension(shape_rank_matched); shape_rank_matched++; - continue; - } - if (binding_info.dimensions[binding_rank_matched] == 1) { binding_rank_matched++; continue; } - if (binding_info.dimensions[binding_rank_matched] == -1) { - // Matches dimension - dims.d[binding_rank_matched] = shape.dimension(shape_rank_matched); + if (shape.dimension(shape_rank_matched) == 1) { shape_rank_matched++; + continue; + } + if (binding_info.dimensions[binding_rank_matched] == 1) { binding_rank_matched++; continue; } @@ -672,7 +680,12 @@ gxf_result_t TensorRtInference::tick() { // Queries binding dimension from context and allocates tensor accordingly const auto& binding_info = binding_infos_[tensor_name]; const auto binding_dims = cuda_engine_->getBindingDimensions(binding_info.index); - gxf::Shape shape{Dims2Dimensions(binding_dims), binding_info.rank}; + + auto dimensions = Dims2Dimensions(binding_dims); + for (int i = 0; i < binding_dims.nbDims; i++) { + if (dimensions[i] < 0) { dimensions[i] = max_batch_size_.get(); } + } + gxf::Shape shape{dimensions, binding_info.rank}; auto result = maybe_result_tensor.value()->reshapeCustom( shape, binding_info.element_type, gxf::PrimitiveTypeSize(binding_info.element_type), diff --git a/isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_inference.hpp b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_inference.hpp similarity index 96% rename from isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_inference.hpp rename to isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_inference.hpp index b12c1cb..d7f2f54 100644 --- a/isaac_ros_tensor_rt/gxf/tensor_rt/tensor_rt_inference.hpp +++ b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/gxf/extensions/tensor_rt/tensor_rt_inference.hpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -30,12 +30,12 @@ #include "gxf/core/entity.hpp" #include "gxf/core/gxf.h" #include "gxf/core/parameter.hpp" +#include "gxf/core/parameter_parser_std.hpp" #include "gxf/cuda/cuda_stream.hpp" #include "gxf/cuda/cuda_stream_pool.hpp" #include "gxf/std/allocator.hpp" #include "gxf/std/clock.hpp" #include "gxf/std/codelet.hpp" -#include "gxf/std/parameter_parser_std.hpp" #include "gxf/std/receiver.hpp" #include "gxf/std/tensor.hpp" #include "gxf/std/transmitter.hpp" @@ -113,6 +113,7 @@ class TensorRtInference : public gxf::Codelet { // Logger instance for TensorRT TensorRTInferenceLogger cuda_logger_; + NvInferHandle infer_runtime_; NvInferHandle cuda_execution_ctx_; NvInferHandle cuda_engine_; diff --git a/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/package.xml b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/package.xml new file mode 100644 index 0000000..391e1a7 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_tensor_rt/package.xml @@ -0,0 +1,47 @@ + + + + + + + gxf_isaac_tensor_rt + 3.0.0 + TensorRT GXF extension. + + Isaac ROS Maintainers + Apache-2.0 + https://developer.nvidia.com/isaac-ros-gems/ + CY Chen + + ament_cmake_auto + + isaac_ros_common + isaac_ros_gxf + gxf_isaac_messages + gxf_isaac_tensorops + + gxf_isaac_gems + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/CMakeLists.txt b/isaac_ros_gxf_extensions/gxf_isaac_triton/CMakeLists.txt new file mode 100644 index 0000000..0b7af41 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/CMakeLists.txt @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.22.1) +project(gxf_isaac_triton LANGUAGES C CXX) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() + +# Determine the architecture +if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + set(GXF_EXT_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/gxf_jetpack60") + set(NVDS_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/gxf_jetpack60") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + set(GXF_EXT_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/gxf_x86_64_cuda_12_2") + set(NVDS_LIB_PATH "${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/gxf_x86_64_cuda_12_2") +else() + message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}") +endif() + +# Add an interface target to export dependencies +add_library(${PROJECT_NAME} INTERFACE) + +# Install shared library +install(FILES "${GXF_EXT_LIB_PATH}/lib${PROJECT_NAME}.so" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/gxf/lib") +set_property(TARGET ${PROJECT_NAME} PROPERTY + INTERFACE_LINK_LIBRARIES + "$/share/${PROJECT_NAME}/gxf/lib/lib${PROJECT_NAME}.so" +) + +# Install headers +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/gxf") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/gxf/" + DESTINATION "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/gxf/include") + set_target_properties(${PROJECT_NAME} PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES + "$/share/${PROJECT_NAME}/gxf/include") +endif() + +install(TARGETS ${PROJECT_NAME} + EXPORT export_${PROJECT_NAME} + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin) +ament_export_targets(export_${PROJECT_NAME} HAS_LIBRARY_TARGET) + +# Install nvds libraries +install(DIRECTORY ${NVDS_LIB_PATH}/ + DESTINATION share/${PROJECT_NAME}/gxf/lib) + +ament_auto_package(INSTALL_TO_SHARE) diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_jetpack60/libgxf_isaac_triton.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_jetpack60/libgxf_isaac_triton.so new file mode 100755 index 0000000..a6a9bed --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_jetpack60/libgxf_isaac_triton.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74d6d9a65e5067b8bee46168ef5e2051f9aa0ff5725df58ef0c77cd31f049888 +size 8608224 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_x86_64_cuda_12_2/libgxf_isaac_triton.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_x86_64_cuda_12_2/libgxf_isaac_triton.so new file mode 100755 index 0000000..9ac1a17 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/lib/gxf_x86_64_cuda_12_2/libgxf_isaac_triton.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:559f1acd9c0efdd5317414aaed9012d85b544d814f4d0617cb46310b3a7bdba4 +size 6099112 diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_custom_process.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_custom_process.h similarity index 98% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_custom_process.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_custom_process.h index 6b25598..52e6616 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_custom_process.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_custom_process.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_datatypes.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_datatypes.h similarity index 64% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_datatypes.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_datatypes.h index 5dbc031..86039f0 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_datatypes.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_datatypes.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2020-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,12 @@ // // SPDX-License-Identifier: Apache-2.0 +/** + * @file infer_datatypes.h + * + * @brief Header file for the data types used in the inference processing. + */ + #ifndef __NVDSINFERSERVER_DATA_TYPES_H__ #define __NVDSINFERSERVER_DATA_TYPES_H__ @@ -34,21 +40,51 @@ namespace nvdsinferserver { +/** + * @brief The type of tensor order. + */ enum class InferTensorOrder : int { kNone = 0, + /** + * @brief NCHW (batch-channels-height-width) tensor order. + */ kLinear = 1, + /** + * @brief NHWC (batch-height-width-channels) tensor order. + */ kNHWC = 2, }; +/** + * @brief The memory types of inference buffers. + */ enum class InferMemType : int { kNone = 0, + /** + * @brief GPU CUDA memory. + */ kGpuCuda = 1, + /** + * @brief Host (CPU) memory. + */ kCpu = 2, + /** + * @brief CUDA pinned memory. + */ kCpuCuda = 3, + /** + * @brief NVRM surface memory. + */ kNvSurface = 5, + /** + * @brief NVRM surface array memory. + */ kNvSurfaceArray = 6, }; +/** + * @brief Datatype of the tensor buffer. + */ enum class InferDataType : int { kFp32 = FLOAT, // 0 kFp16 = HALF, // 1 @@ -58,7 +94,6 @@ enum class InferDataType : int { kUint8, kUint16, kUint32, - // New kFp64, kInt64, kUint64, @@ -67,14 +102,35 @@ enum class InferDataType : int { kNone = -1, }; +/** + * @brief Inference post processing types. + */ enum class InferPostprocessType : int { + /** + * @brief Post processing for object detection. + */ kDetector = 0, + /** + * @brief Post processing for object classification. + */ kClassifier = 1, + /** + * @brief Post processing for image segmentation. + */ kSegmentation = 2, + /** + * @brief Post processing using Triton Classifier. + */ kTrtIsClassifier = 3, + /** + * @brief Custom post processing. + */ kOther = 100, }; +/** + * @brief Image formats. + */ enum class InferMediaFormat : int { /** 24-bit interleaved R-G-B */ kRGB = 0, @@ -89,11 +145,12 @@ enum class InferMediaFormat : int { kUnknown = -1, }; -// typedef NvDsInferDims InferDims; - +/** + * @brief Holds the information about the dimensions of a neural network layer. + */ struct InferDims { - /** Number of dimesions of the layer.*/ + /** Number of dimensions of the layer.*/ unsigned int numDims = 0; /** Size of the layer in each dimension. */ int d[NVDSINFER_MAX_DIMS] = {0}; @@ -110,14 +167,37 @@ struct InferBatchDims InferDims dims; }; +/** + * @brief Holds the information about a inference buffer. + */ struct InferBufferDescription { + /** + * @brief Memory type of the buffer allocation. + */ InferMemType memType; + /** + * @brief Device (GPU) ID where the buffer is allocated. + */ long int devId; + /** + * @brief Datatype associated with the buffer. + */ InferDataType dataType; + /** + * @brief Dimensions of the tensor. + */ InferDims dims; - uint32_t elementSize; // per element bytes, except kString(with elementSize - // is 0) + /** + * @brief Per element bytes, except kString (with elementSize is 0) + */ + uint32_t elementSize; + /** + * @brief Name of the buffer. + */ std::string name; + /** + * @brief Boolean indicating input or output buffer. + */ bool isInput; }; @@ -130,6 +210,9 @@ using SharedIBatchBuffer = std::shared_ptr; using SharedIBatchArray = std::shared_ptr; using SharedIOptions = std::shared_ptr; +/** + * @brief Interface class for a batch buffer. + */ class IBatchBuffer { public: IBatchBuffer() = default; @@ -138,11 +221,15 @@ class IBatchBuffer { virtual void* getBufPtr(uint32_t batchIdx) const = 0; virtual uint32_t getBatchSize() const = 0; virtual uint64_t getTotalBytes() const = 0; + virtual size_t getBufOffset(uint32_t batchIdx) const = 0; private: DISABLE_CLASS_COPY(IBatchBuffer); }; +/** + * @brief Interface class for an array of batch buffers. + */ class IBatchArray { public: IBatchArray() = default; @@ -161,16 +248,6 @@ class IBatchArray { DISABLE_CLASS_COPY(IBatchArray); }; -struct LayerInfo { - InferDataType dataType = InferDataType::kFp32; - InferDims inferDims; - int bindingIndex = 0; - bool isInput = 0; - std::string name; - // New - int maxBatchSize; // 0=> nonBatching -}; - } // namespace nvdsinferserver #endif diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_defines.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_defines.h similarity index 98% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_defines.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_defines.h index b5cea28..c9183ef 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_defines.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_defines.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2020-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_icontext.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_icontext.h similarity index 84% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_icontext.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_icontext.h index 8becc53..22e3425 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_icontext.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_icontext.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2018-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,16 @@ // // SPDX-License-Identifier: Apache-2.0 +/** + * @file infer_icontext.h + * + * @brief Inference context library interface header file. + * + * This file defines the low level library interface for inference using + * Triton inference server. + * + */ + #ifndef __NVDSINFERSERVER_ICONTEXT_H__ #define __NVDSINFERSERVER_ICONTEXT_H__ @@ -33,12 +43,12 @@ namespace nvdsinferserver { /** - * Inference Output callback interface. + * Inference output callback interface. */ using InferOutputCb = std::function; /** - * Inference Logging function interface. + * Inference logging function interface. */ using InferLoggingFunc = std::function; @@ -62,7 +72,7 @@ class IInferContext { const std::string& prototxt, InferLoggingFunc logFunc) = 0; /** - * Run inference relavant processing behind. expect the call is async_mode. + * Run inference relevant processing behind. expect the call is async_mode. * When all behind processing finished. \a done would be called. * * @param[in] input holds all batch buffer array. @@ -99,7 +109,7 @@ extern "C" { /** * Creates a new instance of IInferContext initialized using the supplied - * parameters. + * parameters. The Triton Inference server is accessed in C-API mode. * * @param[in] configStr Parameters to use for initialization of the context. * @param[in] configStrLen use for string length of \a configStr @@ -116,11 +126,19 @@ INFER_EXPORT_API nvdsinferserver::IInferContext* createInferTrtISContext( INFER_EXPORT_API nvdsinferserver::IInferContext* createInferTritonSimpleContext(); +/** + * Creates a new instance of IInferContext initialized using the supplied + * parameters. The Triton Inference server is accessed using gRPC. + * + * @param[in] configStr Parameters to use for initialization of the context. + * @param[in] configStrLen use for string length of \a configStr + * @return new instance of IInferContext. If failed, return nullptr + */ INFER_EXPORT_API nvdsinferserver::IInferContext* createInferTritonGrpcContext(const char* configStr, uint32_t configStrLen); /** - * Creates Triton Server Instance as global singleton. Application need hold it + * Creates Triton Server instance as global singleton. Application need hold it * until no component need triton inference in process. * * @param[in] configStr Parameters for Triton model repo settings. @@ -158,9 +176,9 @@ INFER_EXPORT_API nvdsinferserver::SharedIBatchBuffer NvDsInferServerWrapBuf( std::function freeFunc); /** - * Create a empty BatchArray. + * Create an empty BatchArray. * - * @return A empty Batched array in shared_ptr. If failed, return nullptr. + * @return An empty batched array in shared_ptr. If failed, return nullptr. */ INFER_EXPORT_API nvdsinferserver::SharedIBatchArray NvDsInferServerCreateBatchArray(); @@ -175,7 +193,7 @@ NvDsInferServerCreateBatchArray(); * @param[in] name Tensor name of this buffer. * @param[in] isInput Indicates whether the buffer is for input. It should * always be true for external users. - * @return A Batched Buffer stroing all strings with memtype InferMemType::kCpu. + * @return A batched buffer storing all strings with memtype InferMemType::kCpu. */ INFER_EXPORT_API nvdsinferserver::SharedIBatchBuffer NvDsInferServerCreateStrBuf( diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_ioptions.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_ioptions.h similarity index 98% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_ioptions.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_ioptions.h index 2862760..9e75b3f 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_ioptions.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_ioptions.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_options.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_options.h similarity index 99% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_options.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_options.h index 7b26cbc..76d1b26 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_options.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_options.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/gxf/triton/nvds/include/infer_post_datatypes.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_post_datatypes.h similarity index 98% rename from isaac_ros_triton/gxf/triton/nvds/include/infer_post_datatypes.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_post_datatypes.h index e6bfd14..54b5740 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/infer_post_datatypes.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/infer_post_datatypes.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2020-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinfer.h b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinfer.h similarity index 97% rename from isaac_ros_triton/gxf/triton/nvds/include/nvdsinfer.h rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinfer.h index c8cdd94..70f4593 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinfer.h +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinfer.h @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2017-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2017-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -242,6 +242,8 @@ typedef enum { NVDSINFER_TRITON_ERROR, /** [deprecated]TRT-IS error was encountered */ NVDSINFER_TRTIS_ERROR = NVDSINFER_TRITON_ERROR, + /** Cuda Memory error was encountered. */ + NVDSINFER_MEM_ERROR, /** Unknown error was encountered. */ NVDSINFER_UNKNOWN_ERROR } NvDsInferStatus; @@ -287,14 +289,11 @@ typedef enum /** * Holds full dimensions (including batch size) for a layer. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" typedef struct { int batchSize = 0; NvDsInferDims dims = {0}; } NvDsInferBatchDims; -#pragma GCC diagnostic pop /** * Extended structure for bound layer information which additionally includes diff --git a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_common.proto b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_common.proto similarity index 96% rename from isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_common.proto rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_common.proto index 529e460..6cb399c 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_common.proto +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_common.proto @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2020-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -207,8 +207,12 @@ message ClassificationParams { /** Deepstream segmentation settings */ message SegmentationParams { - /** reserved field */ + /** Segmentation threshold */ float threshold = 1; + /** Number of classes detected by the segmentation network. */ + int32 num_segmentation_classes = 2; + /** Custom function for parsing segmentation output */ + string custom_parse_segmentation_func = 3; } /** Other Network settings, need application to do postprocessing */ diff --git a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_config.proto b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_config.proto similarity index 90% rename from isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_config.proto rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_config.proto index d666901..9ac2aec 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_config.proto +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_config.proto @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2018-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -117,6 +117,14 @@ message TritonModelRepo message TritonGrpcParams { string url = 1; + /** Enable sharing of input CUDA buffers with local Triton server. + * If enabled, the input CUDA buffers are shared with the Triton server + * to improve performance. This feature should be enabled only when the + * Triton server is on the same machine. Applicable for x86 dGPU platform, + * not supported on Jetson devices. + * By default disabled, CUDA buffers are copied to system memory while + * creating the inference request */ + bool enable_cuda_buffer_sharing = 2; } /** Triton inference backend parameters */ @@ -212,5 +220,7 @@ message InferenceConfig { /** LSTM parameters */ LstmParams lstm = 9; } + /** Clip the object bounding-box which lies outside the roi specified by nvdspreprosess plugin. */ + bool clip_object_outside_roi = 11; } diff --git a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_plugin.proto b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_plugin.proto similarity index 94% rename from isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_plugin.proto rename to isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_plugin.proto index 7da0d81..350867f 100644 --- a/isaac_ros_triton/gxf/triton/nvds/include/nvdsinferserver_plugin.proto +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/include/nvdsinferserver_plugin.proto @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2019-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2019-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -100,7 +100,12 @@ message PluginControl { repeated int32 operate_on_class_ids = 3; /** Specifies the number of consecutive, batches to be skipped for * inference. Default is 0. */ - uint32 interval = 4; + oneof inference_interval { + /* For primary inference */ + uint32 interval = 4; + /* For secondary inferrence */ + uint32 secondary_reinfer_interval = 7; + } /** Enables inference on detected objects and asynchronous metadata * attachments. Works only when tracker-id is valid. It's used for * classifier with secondary GIE only. */ diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbuf_fdmap.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbuf_fdmap.so new file mode 100755 index 0000000..b7e024e --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbuf_fdmap.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48b06d6a287c783456301f71723985545a9c1c7453faac20a03b83baa280d775 +size 132200 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurface.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurface.so new file mode 100644 index 0000000..752f861 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurface.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdacbf3b86b0f387ad624c1e5f2266f097e0038567443745314a61f1bf0d44b5 +size 855208 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurftransform.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurftransform.so new file mode 100755 index 0000000..d34bdea --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvbufsurftransform.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cea8d2395c328291be2e6b85b721cc6537f0d8d0ac2b4ed94311313448aed6dd +size 22221568 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_infer_server.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_infer_server.so new file mode 100755 index 0000000..ddeaccc --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_infer_server.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6940f2372a5b44135e2696559d2324c54968b0a5b37a4099622d2536156251b2 +size 11690080 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferlogger.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferlogger.so new file mode 100755 index 0000000..bb05d3c --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferlogger.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:671af0346b3e99e44d8eebb62a36174626a8836bc00fa6b855f50ab3ba926d1e +size 67000 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferutils.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferutils.so new file mode 100755 index 0000000..32a48ba --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_jetpack60/libnvds_inferutils.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3b4c7d1938430a328d619a0c2ce32029b39e8aa08a289477ab74a988d7463cca +size 136304 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbuf_fdmap.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbuf_fdmap.so new file mode 100755 index 0000000..691f923 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbuf_fdmap.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e03cda9e20d388ca6b0eca6cdc58f79eb99d3cc2913d734d1af1e1ec8247d3bd +size 21744 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurface.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurface.so new file mode 100755 index 0000000..ef6bd67 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurface.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a10ef88abc193f624795de7bc3616bd8fcfcdb8cbbba1d1f131e16c7322bb170 +size 38504 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurftransform.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurftransform.so new file mode 100755 index 0000000..5f0176a --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvbufsurftransform.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0fde2ba665dcac14a5f7f91d21c5289994d96715f71341b36ed46ea9380b669 +size 32082936 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_infer_server.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_infer_server.so new file mode 100644 index 0000000..96b2a9d --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_infer_server.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ef46e14b5ee5774689d1dc021161123023646d59cb9b8e56c26f2cb9794c015 +size 21299704 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferlogger.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferlogger.so new file mode 100755 index 0000000..174ec23 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferlogger.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e79baad2a349288cdedb102f0abcde86ce7d27d2fc0494ae09c279d64860e3e3 +size 26080 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferutils.so b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferutils.so new file mode 100755 index 0000000..3262e65 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/nvds/lib/gxf_x86_64_cuda_12_2/libnvds_inferutils.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da819408b4a3f1b21b3c3c9607ceabbf4a31c3ba01536ded15b8cbff904f77da +size 163752 diff --git a/isaac_ros_gxf_extensions/gxf_isaac_triton/package.xml b/isaac_ros_gxf_extensions/gxf_isaac_triton/package.xml new file mode 100644 index 0000000..230ba50 --- /dev/null +++ b/isaac_ros_gxf_extensions/gxf_isaac_triton/package.xml @@ -0,0 +1,43 @@ + + + + + + + gxf_isaac_triton + 3.0.0 + Triton GXF extension. + + Isaac ROS Maintainers + Apache-2.0 + https://developer.nvidia.com/isaac-ros-gems/ + CY Chen + + ament_cmake_auto + + isaac_ros_common + isaac_ros_gxf + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/isaac_ros_tensor_proc/CMakeLists.txt b/isaac_ros_tensor_proc/CMakeLists.txt new file mode 100644 index 0000000..40e37cc --- /dev/null +++ b/isaac_ros_tensor_proc/CMakeLists.txt @@ -0,0 +1,105 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.22.1) +project(isaac_ros_tensor_proc LANGUAGES C CXX CUDA) + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +find_package(ament_cmake_auto REQUIRED) +ament_auto_find_build_dependencies() + +# Interleaved to planar +ament_auto_add_library(interleaved_to_planar_node SHARED src/interleaved_to_planar_node.cpp) +rclcpp_components_register_nodes(interleaved_to_planar_node + "nvidia::isaac_ros::dnn_inference::InterleavedToPlanarNode") +set(node_plugins + "${node_plugins}nvidia::isaac_ros::dnn_inference::InterleavedToPlanarNode;$\n") +set_target_properties(interleaved_to_planar_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE) + + +# Reshape +ament_auto_add_library(reshape_node SHARED src/reshape_node.cpp) +rclcpp_components_register_nodes(reshape_node + "nvidia::isaac_ros::dnn_inference::ReshapeNode") +set(node_plugins + "${node_plugins}nvidia::isaac_ros::dnn_inference::ReshapeNode;$\n") +set_target_properties(reshape_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE) + +# Image to Tensor +ament_auto_add_library(image_to_tensor_node SHARED src/image_to_tensor_node.cpp) +rclcpp_components_register_nodes(image_to_tensor_node + "nvidia::isaac_ros::dnn_inference::ImageToTensorNode") +set(node_plugins + "${node_plugins}nvidia::isaac_ros::dnn_inference::ImageToTensorNode;$\n") +target_link_libraries(image_to_tensor_node cvcuda nvcv_types) +set_target_properties(image_to_tensor_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE) + +# Image tensor normalize node +ament_auto_add_library(image_tensor_normalize_node SHARED src/image_tensor_normalize_node.cpp) + rclcpp_components_register_nodes(image_tensor_normalize_node + "nvidia::isaac_ros::dnn_inference::ImageTensorNormalizeNode") + set(node_plugins + "${node_plugins}nvidia::isaac_ros::dnn_inference::ImageTensorNormalizeNode;$\n") + target_link_libraries(image_tensor_normalize_node cvcuda nvcv_types) + set_target_properties(image_tensor_normalize_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE) + +ament_auto_add_library(normalize_node SHARED src/normalize_node.cpp) + rclcpp_components_register_nodes(normalize_node + "nvidia::isaac_ros::dnn_inference::NormalizeNode") + set(node_plugins + "${node_plugins}nvidia::isaac_ros::dnn_inference::NormalizeNode;$\n") + set_target_properties(normalize_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE) + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() + + + # The FindPythonInterp and FindPythonLibs modules are removed + if(POLICY CMP0148) + cmake_policy(SET CMP0148 OLD) + endif() + + find_package(launch_testing_ament_cmake REQUIRED) + add_launch_test(test/isaac_ros_interleaved_to_planar_test.py) + add_launch_test(test/isaac_ros_reshape_test.py) + add_launch_test(test/isaac_ros_image_to_tensor.py) + add_launch_test(test/isaac_ros_image_to_tensor_no_scale.py) + add_launch_test(test/isaac_ros_image_to_tensor_no_scale_float.py) + add_launch_test(test/isaac_ros_image_tensor_normalize_test.py) + add_launch_test(test/isaac_ros_normalize_test.py) +endif() + +ament_auto_package(INSTALL_TO_SHARE config) diff --git a/isaac_ros_tensor_proc/config/interleaved_to_planar_node.yaml b/isaac_ros_tensor_proc/config/interleaved_to_planar_node.yaml new file mode 100644 index 0000000..0c7b74b --- /dev/null +++ b/isaac_ros_tensor_proc/config/interleaved_to_planar_node.yaml @@ -0,0 +1,122 @@ +%YAML 1.2 +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: global +components: +- name: adapter_video_buffer + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "VideoBuffer" +- name: adapter_bgr_u8 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "BGR_U8" +- name: adapter_rgb_u8 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "RGB_U8" +- name: adapter_bgr_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "BGR_F32" +- name: adapter_rgb_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "RGB_F32" +- name: adapter_planar_bgr_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "PLANAR_BGR_F32" +- name: adapter_planar_rgb_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "PLANAR_RGB_F32" +--- +name: interleaved_to_planar +components: +- name: data_receiver + type: nvidia::gxf::DoubleBufferReceiver + parameters: + capacity: 12 +- type: nvidia::gxf::MessageAvailableSchedulingTerm + parameters: + receiver: data_receiver + min_size: 1 +- name: data_transmitter + type: nvidia::gxf::DoubleBufferTransmitter + parameters: + capacity: 12 +- type: nvidia::gxf::DownstreamReceptiveSchedulingTerm + parameters: + transmitter: data_transmitter + min_size: 1 +- name: allocator + type: nvidia::gxf::BlockMemoryPool + parameters: + storage_type: 1 + block_size: 0 + num_blocks: 40 +- name: interleaved_to_planar_operator + type: nvidia::isaac::tensor_ops::InterleavedToPlanar + parameters: + receiver: data_receiver + transmitter: data_transmitter + pool: allocator + input_adapter: global/adapter_rgb_f32 # nhwc + output_adapter: global/adapter_planar_rgb_f32 # nchw + output_name: "input_tensor" +--- +name: sink +components: +- name: signal + type: nvidia::gxf::DoubleBufferReceiver + parameters: + capacity: 1 +- type: nvidia::gxf::MessageAvailableSchedulingTerm + parameters: + receiver: signal + min_size: 1 +- name: sink + type: nvidia::isaac_ros::MessageRelay + parameters: + source: signal +--- +components: +- type: nvidia::gxf::Connection + parameters: + source: interleaved_to_planar/data_transmitter + target: sink/signal +--- +name: utils +components: +- name: clock + type: nvidia::gxf::RealtimeClock +- type: nvidia::gxf::EventBasedScheduler + parameters: + clock: clock + stop_on_deadlock: false + worker_thread_number: 2 +- type: nvidia::gxf::JobStatistics + parameters: + clock: clock diff --git a/isaac_ros_tensor_proc/config/namespace_injector_rule_interleaved_to_planar.yaml b/isaac_ros_tensor_proc/config/namespace_injector_rule_interleaved_to_planar.yaml new file mode 100644 index 0000000..0bb7442 --- /dev/null +++ b/isaac_ros_tensor_proc/config/namespace_injector_rule_interleaved_to_planar.yaml @@ -0,0 +1,24 @@ +%YAML 1.2 +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: Interleaved To Planar Namespace Injector Rule +operation: namespace_injector +body: + components: + - type: nvidia::isaac::tensor_ops::InterleavedToPlanar + path_parameter_keys: [input_adapter, output_adapter] diff --git a/isaac_ros_tensor_proc/config/namespace_injector_rule_normalizer.yaml b/isaac_ros_tensor_proc/config/namespace_injector_rule_normalizer.yaml new file mode 100644 index 0000000..e6e4000 --- /dev/null +++ b/isaac_ros_tensor_proc/config/namespace_injector_rule_normalizer.yaml @@ -0,0 +1,24 @@ +%YAML 1.2 +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: Normalizer Namespace Injector Rule +operation: namespace_injector +body: + components: + - type: nvidia::isaac::tensor_ops::Normalize + path_parameter_keys: [input_adapter, output_adapter] diff --git a/isaac_ros_tensor_proc/config/namespace_injector_rule_reshaper.yaml b/isaac_ros_tensor_proc/config/namespace_injector_rule_reshaper.yaml new file mode 100644 index 0000000..212a637 --- /dev/null +++ b/isaac_ros_tensor_proc/config/namespace_injector_rule_reshaper.yaml @@ -0,0 +1,24 @@ +%YAML 1.2 +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: Reshaper Namespace Injector Rule +operation: namespace_injector +body: + components: + - type: nvidia::isaac::tensor_ops::Reshape + path_parameter_keys: [input_adapter, output_adapter] diff --git a/isaac_ros_tensor_proc/config/normalizer_node.yaml b/isaac_ros_tensor_proc/config/normalizer_node.yaml new file mode 100644 index 0000000..3ccb7be --- /dev/null +++ b/isaac_ros_tensor_proc/config/normalizer_node.yaml @@ -0,0 +1,124 @@ +%YAML 1.2 +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: global +components: +- name: adapter_video_buffer + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "VideoBuffer" +- name: adapter_bgr_u8 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "BGR_U8" +- name: adapter_rgb_u8 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "RGB_U8" +- name: adapter_bgr_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "BGR_F32" +- name: adapter_rgb_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "RGB_F32" +- name: adapter_planar_bgr_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "PLANAR_BGR_F32" +- name: adapter_planar_rgb_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "PLANAR_RGB_F32" +--- +name: normalizer +components: +- name: data_receiver + type: nvidia::gxf::DoubleBufferReceiver + parameters: + capacity: 12 +- type: nvidia::gxf::MessageAvailableSchedulingTerm + parameters: + receiver: data_receiver + min_size: 1 +- name: data_transmitter + type: nvidia::gxf::DoubleBufferTransmitter + parameters: + capacity: 12 +- type: nvidia::gxf::DownstreamReceptiveSchedulingTerm + parameters: + transmitter: data_transmitter + min_size: 1 +- name: allocator + type: nvidia::gxf::BlockMemoryPool + parameters: + storage_type: 1 + block_size: 0 + num_blocks: 40 +- name: normalizer_operator + type: nvidia::isaac::tensor_ops::Normalize + parameters: + scales: [ 0.0156862745, 0.00490196078, 0.00784313725 ] + offsets: [ -127.5, -153.0, -63.75 ] + receiver: data_receiver + transmitter: data_transmitter + pool: allocator + input_adapter: global/adapter_video_buffer + output_adapter: global/adapter_rgb_f32 + output_name: "image" +--- +name: sink +components: +- name: signal + type: nvidia::gxf::DoubleBufferReceiver + parameters: + capacity: 1 +- type: nvidia::gxf::MessageAvailableSchedulingTerm + parameters: + receiver: signal + min_size: 1 +- name: sink + type: nvidia::isaac_ros::MessageRelay + parameters: + source: signal +--- +components: +- type: nvidia::gxf::Connection + parameters: + source: normalizer/data_transmitter + target: sink/signal +--- +name: utils +components: +- name: clock + type: nvidia::gxf::RealtimeClock +- type: nvidia::gxf::EventBasedScheduler + parameters: + clock: clock + stop_on_deadlock: false + worker_thread_number: 2 +- type: nvidia::gxf::JobStatistics + parameters: + clock: clock diff --git a/isaac_ros_tensor_proc/config/reshape_node.yaml b/isaac_ros_tensor_proc/config/reshape_node.yaml new file mode 100644 index 0000000..990a681 --- /dev/null +++ b/isaac_ros_tensor_proc/config/reshape_node.yaml @@ -0,0 +1,127 @@ +%YAML 1.2 +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +--- +name: global +components: +- name: adapter_video_buffer + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "VideoBuffer" +- name: adapter_bgr_u8 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "BGR_U8" +- name: adapter_rgb_u8 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "RGB_U8" +- name: adapter_bgr_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "BGR_F32" +- name: adapter_rgb_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "RGB_F32" +- name: adapter_planar_bgr_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "PLANAR_BGR_F32" +- name: adapter_planar_rgb_f32 + type: nvidia::isaac::tensor_ops::ImageAdapter + parameters: + message_type: "Tensor" + image_type: "PLANAR_RGB_F32" +--- +name: reshaper +components: +- name: data_receiver + type: nvidia::gxf::DoubleBufferReceiver + parameters: + capacity: 12 +- type: nvidia::gxf::MessageAvailableSchedulingTerm + parameters: + receiver: data_receiver + min_size: 1 +- name: data_transmitter + type: nvidia::gxf::DoubleBufferTransmitter + parameters: + capacity: 12 +- type: nvidia::gxf::DownstreamReceptiveSchedulingTerm + parameters: + transmitter: data_transmitter + min_size: 1 +- name: allocator + type: nvidia::gxf::BlockMemoryPool + parameters: + storage_type: 1 + block_size: 0 + num_blocks: 40 +- type: nvidia::gxf::MemoryAvailableSchedulingTerm + parameters: + allocator: allocator + min_blocks: 1 +- name: reshape_operator + type: nvidia::isaac::tensor_ops::Reshape + parameters: + receiver: data_receiver + transmitter: data_transmitter + pool: allocator + input_adapter: global/adapter_planar_rgb_f32 + output_adapter: global/adapter_planar_rgb_f32 + output_name: "input_tensor" + output_shape: [] +--- +name: sink +components: +- name: signal + type: nvidia::gxf::DoubleBufferReceiver + parameters: + capacity: 1 +- type: nvidia::gxf::MessageAvailableSchedulingTerm + parameters: + receiver: signal + min_size: 1 +- name: sink + type: nvidia::isaac_ros::MessageRelay + parameters: + source: signal +--- +components: +- type: nvidia::gxf::Connection + parameters: + source: reshaper/data_transmitter + target: sink/signal +--- +name: utils +components: +- name: clock + type: nvidia::gxf::RealtimeClock +- type: nvidia::gxf::EventBasedScheduler + parameters: + clock: clock + stop_on_deadlock: false + worker_thread_number: 2 +- type: nvidia::gxf::JobStatistics + parameters: + clock: clock diff --git a/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_tensor_normalize_node.hpp b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_tensor_normalize_node.hpp new file mode 100644 index 0000000..5ddbcc0 --- /dev/null +++ b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_tensor_normalize_node.hpp @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ISAAC_ROS_TENSOR_PROC__IMAGE_TENSOR_NORMALIZE_NODE_HPP_ +#define ISAAC_ROS_TENSOR_PROC__IMAGE_TENSOR_NORMALIZE_NODE_HPP_ + +#include +#include +#include + +#include "cvcuda/OpNormalize.hpp" +#include "isaac_ros_common/qos.hpp" +#include "isaac_ros_managed_nitros/managed_nitros_publisher.hpp" +#include "isaac_ros_managed_nitros/managed_nitros_subscriber.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list_view.hpp" +#include "nvcv/Tensor.hpp" +#include "rclcpp/rclcpp.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +class ImageTensorNormalizeNode : public rclcpp::Node +{ +public: + explicit ImageTensorNormalizeNode(const rclcpp::NodeOptions options = rclcpp::NodeOptions()); + + ~ImageTensorNormalizeNode(); + +private: + void ImageTensorNormalizeCallback( + const ::nvidia::isaac_ros::nitros::NitrosTensorListView & tensor_msg); + + rclcpp::QoS input_qos_; + rclcpp::QoS output_qos_; + + std::shared_ptr<::nvidia::isaac_ros::nitros::ManagedNitrosSubscriber< + ::nvidia::isaac_ros::nitros::NitrosTensorListView>> + nitros_tensor_sub_; + std::shared_ptr<::nvidia::isaac_ros::nitros::ManagedNitrosPublisher< + ::nvidia::isaac_ros::nitros::NitrosTensorList>> + nitros_tensor_pub_; + + const std::vector mean_param_; + const std::vector stddev_param_; + + const std::string input_tensor_name_; + const std::string output_tensor_name_; + + nvcv::Tensor mean_; + nvcv::Tensor stddev_; + + cudaStream_t stream_; + nvcv::TensorLayout tensor_layout_; + cvcuda::Normalize norm_op_; +}; + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#endif // ISAAC_ROS_TENSOR_PROC__IMAGE_TENSOR_NORMALIZE_NODE_HPP_ diff --git a/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_to_tensor_node.hpp b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_to_tensor_node.hpp new file mode 100644 index 0000000..2fcfe48 --- /dev/null +++ b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/image_to_tensor_node.hpp @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#ifndef ISAAC_ROS_TENSOR_PROC__IMAGE_TO_TENSOR_NODE_HPP_ +#define ISAAC_ROS_TENSOR_PROC__IMAGE_TO_TENSOR_NODE_HPP_ + +#include +#include + +#include "cvcuda/OpConvertTo.hpp" +#include "isaac_ros_common/qos.hpp" +#include "isaac_ros_managed_nitros/managed_nitros_publisher.hpp" +#include "isaac_ros_managed_nitros/managed_nitros_subscriber.hpp" +#include "isaac_ros_nitros_image_type/nitros_image_view.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list_view.hpp" +#include "nvcv/Tensor.hpp" +#include "rclcpp/rclcpp.hpp" + + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +class ImageToTensorNode : public rclcpp::Node +{ +public: + explicit ImageToTensorNode(const rclcpp::NodeOptions options = rclcpp::NodeOptions()); + ~ImageToTensorNode(); + +private: + void ImageToTensorCallback(const ::nvidia::isaac_ros::nitros::NitrosImageView & img_msg); + + rclcpp::QoS input_qos_; + rclcpp::QoS output_qos_; + + std::shared_ptr<::nvidia::isaac_ros::nitros::ManagedNitrosSubscriber< + ::nvidia::isaac_ros::nitros::NitrosImageView>> + nitros_img_sub_; + + std::shared_ptr<::nvidia::isaac_ros::nitros::ManagedNitrosPublisher< + ::nvidia::isaac_ros::nitros::NitrosTensorList>> + nitros_tensor_pub_; + + bool scale_; + std::string tensor_name_; + cudaStream_t stream_; + + cvcuda::ConvertTo convert_op_; +}; + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#endif // ISAAC_ROS_TENSOR_PROC__IMAGE_TO_TENSOR_NODE_HPP_ diff --git a/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/interleaved_to_planar_node.hpp b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/interleaved_to_planar_node.hpp new file mode 100644 index 0000000..509c32c --- /dev/null +++ b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/interleaved_to_planar_node.hpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +#ifndef ISAAC_ROS_TENSOR_PROC__INTERLEAVED_TO_PLANAR_NODE_HPP_ +#define ISAAC_ROS_TENSOR_PROC__INTERLEAVED_TO_PLANAR_NODE_HPP_ + +#include +#include + +#include "isaac_ros_nitros/nitros_node.hpp" +#include "rclcpp/rclcpp.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +class InterleavedToPlanarNode : public nitros::NitrosNode +{ +public: + explicit InterleavedToPlanarNode(const rclcpp::NodeOptions & = rclcpp::NodeOptions()); + ~InterleavedToPlanarNode() = default; + + void postLoadGraphCallback() override; + +private: + std::vector input_tensor_shape_; + int64_t num_blocks_; + std::string output_tensor_name_; +}; +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + + +#endif // ISAAC_ROS_TENSOR_PROC__INTERLEAVED_TO_PLANAR_NODE_HPP_ diff --git a/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/normalize_node.hpp b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/normalize_node.hpp new file mode 100644 index 0000000..9853e7b --- /dev/null +++ b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/normalize_node.hpp @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +#ifndef ISAAC_ROS_TENSOR_PROC__NORMALIZE_NODE_HPP_ +#define ISAAC_ROS_TENSOR_PROC__NORMALIZE_NODE_HPP_ + +#include +#include + +#include "isaac_ros_nitros/nitros_node.hpp" +#include "rclcpp/rclcpp.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +class NormalizeNode : public nitros::NitrosNode +{ +public: + explicit NormalizeNode(const rclcpp::NodeOptions & = rclcpp::NodeOptions()); + ~NormalizeNode() = default; + + void preLoadGraphCallback() override; + void postLoadGraphCallback() override; + +private: + const std::vector image_mean_; + const std::vector image_stddev_; + uint16_t input_image_width_; + uint16_t input_image_height_; + int64_t num_blocks_; + std::string output_tensor_name_; +}; +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#endif // ISAAC_ROS_TENSOR_PROC__NORMALIZE_NODE_HPP_ diff --git a/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/reshape_node.hpp b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/reshape_node.hpp new file mode 100644 index 0000000..ffd969f --- /dev/null +++ b/isaac_ros_tensor_proc/include/isaac_ros_tensor_proc/reshape_node.hpp @@ -0,0 +1,51 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +#ifndef ISAAC_ROS_TENSOR_PROC__RESHAPE_NODE_HPP_ +#define ISAAC_ROS_TENSOR_PROC__RESHAPE_NODE_HPP_ + +#include +#include + +#include "isaac_ros_nitros/nitros_node.hpp" +#include "rclcpp/rclcpp.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +class ReshapeNode : public nitros::NitrosNode +{ +public: + explicit ReshapeNode(const rclcpp::NodeOptions & = rclcpp::NodeOptions()); + ~ReshapeNode() = default; + + void postLoadGraphCallback() override; + +private: + std::string output_tensor_name_; + std::vector input_tensor_shape_; + std::vector output_tensor_shape_; + int64_t num_blocks_; +}; +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#endif // ISAAC_ROS_TENSOR_PROC__RESHAPE_NODE_HPP_ diff --git a/isaac_ros_tensor_proc/package.xml b/isaac_ros_tensor_proc/package.xml new file mode 100644 index 0000000..664f766 --- /dev/null +++ b/isaac_ros_tensor_proc/package.xml @@ -0,0 +1,54 @@ + + + + + + + isaac_ros_tensor_proc + 3.0.0 + Processing operations for tensors + Isaac ROS Maintainers + Apache-2.0 + https://developer.nvidia.com/isaac-ros-gems/ + Ethan Yu + Kajanan Chinniah + Swapnesh Wani + + rclcpp + rclcpp_components + isaac_ros_image_proc + isaac_ros_nitros + isaac_ros_nitros_image_type + isaac_ros_nitros_tensor_list_type + isaac_ros_managed_nitros + + isaac_ros_common + isaac_ros_gxf + + gxf_isaac_message_compositor + gxf_isaac_tensorops + + ament_lint_auto + ament_lint_common + isaac_ros_test + + + ament_cmake + + diff --git a/isaac_ros_tensor_proc/src/image_tensor_normalize_node.cpp b/isaac_ros_tensor_proc/src/image_tensor_normalize_node.cpp new file mode 100644 index 0000000..e584cf2 --- /dev/null +++ b/isaac_ros_tensor_proc/src/image_tensor_normalize_node.cpp @@ -0,0 +1,232 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "isaac_ros_tensor_proc/image_tensor_normalize_node.hpp" + +#include +#include +#include + +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_builder.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list_builder.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_shape.hpp" +#include "nvcv/TensorDataAccess.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +namespace +{ + +nvcv::TensorLayout StrToTensorLayout(const std::string & str) +{ + // NOTE: cvcuda seems to not work with other formats + static const std::unordered_map str_to_tensor_layout = { + {"HWC", nvcv::TENSOR_HWC}, + }; + return str_to_tensor_layout.at(str); +} + +inline void CheckCudaErrors(cudaError_t code, const char * file, const int line) +{ + if (code != cudaSuccess) { + const std::string message = "CUDA error returned at " + std::string(file) + ":" + + std::to_string(line) + ", Error code: " + std::to_string(code) + + " (" + std::string(cudaGetErrorString(code)) + ")"; + throw std::runtime_error(message); + } +} + +nvcv::TensorShape::ShapeType GetShape( + const ::nvidia::isaac_ros::nitros::NitrosTensorShape & shape, + const nvcv::TensorLayout & tensor_layout) +{ + if (tensor_layout == nvcv::TENSOR_HWC || tensor_layout == nvcv::TENSOR_CHW) { + return {shape.shape().dimension(0), shape.shape().dimension(1), shape.shape().dimension(2)}; + } else if (tensor_layout == nvcv::TENSOR_NHWC || tensor_layout == nvcv::TENSOR_NCHW) { + return { + shape.shape().dimension(0), shape.shape().dimension(1), shape.shape().dimension(2), + shape.shape().dimension(3)}; + } + throw std::invalid_argument("Error: received unexpected tensor format!"); +} + + +void ComputeBufferStrides( + const ::nvidia::isaac_ros::nitros::NitrosTensorShape & shape, + const nvcv::TensorLayout & tensor_layout, + const size_t bytes_per_element, + nvcv::TensorDataStridedCuda::Buffer & buffer) +{ + // Manually compute strides, we should get this from CUDA with NITROS later + if (tensor_layout == nvcv::TENSOR_HWC || tensor_layout == nvcv::TENSOR_CHW) { + buffer.strides[2] = bytes_per_element; + buffer.strides[1] = shape.shape().dimension(2) * buffer.strides[2]; + buffer.strides[0] = shape.shape().dimension(1) * buffer.strides[1]; + } else if (tensor_layout == nvcv::TENSOR_NHWC || tensor_layout == nvcv::TENSOR_NCHW) { + buffer.strides[3] = bytes_per_element; + buffer.strides[2] = shape.shape().dimension(3) * buffer.strides[3]; + buffer.strides[1] = shape.shape().dimension(2) * buffer.strides[2]; + buffer.strides[0] = shape.shape().dimension(1) * buffer.strides[1]; + } +} + +void ToCVCudaTensor( + const ::nvidia::isaac_ros::nitros::NitrosTensorListView::NitrosTensorView & tensor, + const nvcv::TensorLayout & tensor_layout, nvcv::Tensor & cv_cuda_tensor) +{ + nvcv::TensorDataStridedCuda::Buffer buffer; + ComputeBufferStrides(tensor.GetShape(), tensor_layout, tensor.GetBytesPerElement(), buffer); + buffer.basePtr = const_cast(reinterpret_cast(tensor.GetBuffer())); + nvcv::TensorShape::ShapeType shape{GetShape(tensor.GetShape(), tensor_layout)}; + nvcv::TensorShape tensor_shape{shape, tensor_layout}; + + auto tensor_type = [&]() { + switch (tensor.GetElementType()) { + case nvidia::isaac_ros::nitros::PrimitiveType::kUnsigned8: + return nvcv::TYPE_U8; + case nvidia::isaac_ros::nitros::PrimitiveType::kFloat32: + return nvcv::TYPE_F32; + default: + throw std::invalid_argument("Received unexpected type!"); + } + }(); + nvcv::TensorDataStridedCuda data{tensor_shape, tensor_type, buffer}; + cv_cuda_tensor = nvcv::TensorWrapData(data); +} + +void ToCVCudaTensor( + float * tensor, const ::nvidia::isaac_ros::nitros::NitrosTensorShape & shape, + const nvcv::TensorLayout & tensor_layout, nvcv::Tensor & cv_cuda_tensor) +{ + nvcv::TensorDataStridedCuda::Buffer buffer; + ComputeBufferStrides(shape, tensor_layout, sizeof(float), buffer); + buffer.basePtr = const_cast(reinterpret_cast(tensor)); + nvcv::TensorShape::ShapeType cv_shape{GetShape(shape, tensor_layout)}; + nvcv::TensorShape tensor_shape{cv_shape, tensor_layout}; + nvcv::TensorDataStridedCuda data{tensor_shape, nvcv::TYPE_F32, buffer}; + cv_cuda_tensor = nvcv::TensorWrapData(data); +} + +} // namespace + +ImageTensorNormalizeNode::ImageTensorNormalizeNode(const rclcpp::NodeOptions options) +: rclcpp::Node("image_tensor_normalize_node", options), + input_qos_{::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "input_qos")}, + output_qos_{::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "output_qos")}, + nitros_tensor_sub_{std::make_shared<::nvidia::isaac_ros::nitros::ManagedNitrosSubscriber< + ::nvidia::isaac_ros::nitros::NitrosTensorListView>>( + this, "tensor", + nvidia::isaac_ros::nitros::nitros_tensor_list_nhwc_rgb_f32_t::supported_type_name, + std::bind(&ImageTensorNormalizeNode::ImageTensorNormalizeCallback, this, + std::placeholders::_1), nvidia::isaac_ros::nitros::NitrosStatisticsConfig{}, input_qos_)}, + nitros_tensor_pub_{std::make_shared< + nvidia::isaac_ros::nitros::ManagedNitrosPublisher< + nvidia::isaac_ros::nitros::NitrosTensorList>>( + this, "normalized_tensor", + nvidia::isaac_ros::nitros::nitros_tensor_list_nhwc_rgb_f32_t::supported_type_name, + nvidia::isaac_ros::nitros::NitrosStatisticsConfig{}, output_qos_)}, + mean_param_{declare_parameter>("mean", {0.5, 0.5, 0.5})}, + stddev_param_{declare_parameter>("stddev", {0.5, 0.5, 0.5})}, + input_tensor_name_{declare_parameter("input_tensor_name", "tensor")}, + output_tensor_name_{declare_parameter("output_tensor_name", "tensor")}, + tensor_layout_{StrToTensorLayout("HWC")} +{ + CheckCudaErrors(cudaStreamCreate(&stream_), __FILE__, __LINE__); + std::vector mean_float(mean_param_.begin(), mean_param_.end()); + std::vector stddev_float(stddev_param_.begin(), stddev_param_.end()); + nvcv::TensorShape::ShapeType shape{nvcv::TensorShape::ShapeType{1, 1, 1, + static_cast(mean_param_.size())}}; + nvcv::TensorShape tensor_shape{shape, nvcv::TENSOR_NHWC}; + + mean_ = nvcv::Tensor(tensor_shape, nvcv::TYPE_F32); + stddev_ = nvcv::Tensor(tensor_shape, nvcv::TYPE_F32); + + auto mean_data = mean_.exportData(); + auto mean_access = nvcv::TensorDataAccessStridedImagePlanar::Create(*mean_data); + auto stddev_data = stddev_.exportData(); + auto stddev_access = nvcv::TensorDataAccessStridedImagePlanar::Create(*stddev_data); + + CheckCudaErrors( + cudaMemcpy2D( + mean_access->sampleData(0), mean_access->rowStride(), mean_float.data(), + mean_float.size() * sizeof(float), mean_float.size() * sizeof(float), 1, + cudaMemcpyHostToDevice), + __FILE__, __LINE__); + CheckCudaErrors( + cudaMemcpy2D( + stddev_access->sampleData(0), stddev_access->rowStride(), stddev_float.data(), + stddev_float.size() * sizeof(float), stddev_float.size() * sizeof(float), 1, + cudaMemcpyHostToDevice), + __FILE__, __LINE__); +} + +void ImageTensorNormalizeNode::ImageTensorNormalizeCallback( + const ::nvidia::isaac_ros::nitros::NitrosTensorListView & tensor_msg) +{ + const auto tensor = tensor_msg.GetNamedTensor(input_tensor_name_); + nvcv::Tensor input_tensor; + ToCVCudaTensor(tensor, tensor_layout_, input_tensor); + + float * raw_output_buffer{nullptr}; + const size_t output_buffer_size{tensor.GetElementCount() * sizeof(float)}; + CheckCudaErrors( + cudaMallocAsync(&raw_output_buffer, output_buffer_size, stream_), __FILE__, __LINE__); + nvcv::Tensor output_tensor; + ToCVCudaTensor(raw_output_buffer, tensor.GetShape(), tensor_layout_, output_tensor); + + norm_op_( + stream_, input_tensor, mean_, stddev_, output_tensor, 1.0f, 0.0f, 0.0f, + CVCUDA_NORMALIZE_SCALE_IS_STDDEV); + + CheckCudaErrors(cudaStreamSynchronize(stream_), __FILE__, __LINE__); + + std_msgs::msg::Header header; + header.frame_id = tensor_msg.GetFrameId(); + header.stamp.sec = tensor_msg.GetTimestampSeconds(); + header.stamp.nanosec = tensor_msg.GetTimestampNanoseconds(); + + nvidia::isaac_ros::nitros::NitrosTensorList tensor_list = + nvidia::isaac_ros::nitros::NitrosTensorListBuilder() + .WithHeader(header) + .AddTensor( + output_tensor_name_, (nvidia::isaac_ros::nitros::NitrosTensorBuilder() + .WithShape(tensor.GetShape()) + .WithDataType(nvidia::isaac_ros::nitros::NitrosDataType::kFloat32) + .WithData(raw_output_buffer) + .Build())) + .Build(); + + nitros_tensor_pub_->publish(tensor_list); +} + +ImageTensorNormalizeNode::~ImageTensorNormalizeNode() +{ + CheckCudaErrors(cudaStreamDestroy(stream_), __FILE__, __LINE__); +} + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#include "rclcpp_components/register_node_macro.hpp" +RCLCPP_COMPONENTS_REGISTER_NODE(nvidia::isaac_ros::dnn_inference::ImageTensorNormalizeNode) diff --git a/isaac_ros_tensor_proc/src/image_to_tensor_node.cpp b/isaac_ros_tensor_proc/src/image_to_tensor_node.cpp new file mode 100644 index 0000000..50d60ee --- /dev/null +++ b/isaac_ros_tensor_proc/src/image_to_tensor_node.cpp @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "isaac_ros_tensor_proc/image_to_tensor_node.hpp" + +#include + +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_builder.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list_builder.hpp" +#include "sensor_msgs/image_encodings.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ + +namespace +{ + +inline void CheckCudaErrors(cudaError_t code, const char * file, const int line) +{ + if (code != cudaSuccess) { + const std::string message = "CUDA error returned at " + std::string(file) + ":" + + std::to_string(line) + ", Error code: " + std::to_string(code) + + " (" + std::string(cudaGetErrorString(code)) + ")"; + throw std::runtime_error(message); + } +} + +struct NVCVImageFormat +{ + nvcv::ImageFormat interleaved_format; + nvcv::ImageFormat interleaved_float_format; +}; + +NVCVImageFormat ToNVCVFormat(const std::string & image_encoding) +{ + static const std::unordered_map str_to_nvcv_format({ + {sensor_msgs::image_encodings::RGB8, NVCVImageFormat{nvcv::FMT_RGB8, nvcv::FMT_RGBf32}}, + {sensor_msgs::image_encodings::BGR8, NVCVImageFormat{nvcv::FMT_BGR8, nvcv::FMT_BGRf32}}, + {sensor_msgs::image_encodings::RGBA8, + NVCVImageFormat{nvcv::FMT_RGBA8, nvcv::FMT_RGBAf32}}, + {sensor_msgs::image_encodings::BGRA8, + NVCVImageFormat{nvcv::FMT_BGRA8, nvcv::FMT_BGRAf32}}, + {sensor_msgs::image_encodings::MONO8, NVCVImageFormat{nvcv::FMT_U8, nvcv::FMT_F32}}, + {sensor_msgs::image_encodings::TYPE_32FC1, + NVCVImageFormat{nvcv::FMT_F32, nvcv::FMT_F32}}, + // NOTE: cvcuda doesn't define generic 3 channel types for floats + {sensor_msgs::image_encodings::TYPE_32FC3, + NVCVImageFormat{nvcv::FMT_RGBf32, nvcv::FMT_RGBf32}}, + {sensor_msgs::image_encodings::TYPE_32FC4, + NVCVImageFormat{nvcv::FMT_RGBAf32, nvcv::FMT_RGBAf32}}, + }); + return str_to_nvcv_format.at(image_encoding); +} + +constexpr size_t kBatchSize{1}; + +} // namespace + +ImageToTensorNode::ImageToTensorNode(const rclcpp::NodeOptions options) +: rclcpp::Node("image_to_tensor_node", options), + input_qos_{::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "input_qos")}, + output_qos_{::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "output_qos")}, + nitros_img_sub_{std::make_shared<::nvidia::isaac_ros::nitros::ManagedNitrosSubscriber< + ::nvidia::isaac_ros::nitros::NitrosImageView>>( + this, "image", ::nvidia::isaac_ros::nitros::nitros_image_rgb8_t::supported_type_name, + std::bind(&ImageToTensorNode::ImageToTensorCallback, this, + std::placeholders::_1), nvidia::isaac_ros::nitros::NitrosStatisticsConfig{}, + input_qos_)}, + nitros_tensor_pub_{std::make_shared< + nvidia::isaac_ros::nitros::ManagedNitrosPublisher< + nvidia::isaac_ros::nitros::NitrosTensorList>>( + this, "tensor", + nvidia::isaac_ros::nitros::nitros_tensor_list_nchw_rgb_f32_t::supported_type_name, + nvidia::isaac_ros::nitros::NitrosStatisticsConfig{}, output_qos_)}, + scale_{declare_parameter("scale", true)}, + tensor_name_{declare_parameter("tensor_name", "tensor")} +{ + CheckCudaErrors(cudaStreamCreate(&stream_), __FILE__, __LINE__); +} + +void ImageToTensorNode::ImageToTensorCallback( + const ::nvidia::isaac_ros::nitros::NitrosImageView & img_msg) +{ + const uint32_t img_width{img_msg.GetWidth()}; + const uint32_t img_height{img_msg.GetHeight()}; + const int img_channels{sensor_msgs::image_encodings::numChannels(img_msg.GetEncoding())}; + const NVCVImageFormat format = ToNVCVFormat(img_msg.GetEncoding()); + + nvcv::TensorDataStridedCuda::Buffer input_buffer; + input_buffer.strides[3] = + sensor_msgs::image_encodings::bitDepth(img_msg.GetEncoding()) / CHAR_BIT; + input_buffer.strides[2] = img_channels * input_buffer.strides[3]; + input_buffer.strides[1] = img_msg.GetStride(); + input_buffer.strides[0] = img_msg.GetHeight() * input_buffer.strides[1]; + + input_buffer.basePtr = + const_cast(reinterpret_cast(img_msg.GetGpuData())); + + nvcv::Tensor::Requirements input_reqs{nvcv::Tensor::CalcRequirements( + kBatchSize, + {static_cast(img_msg.GetWidth()), + static_cast(img_msg.GetHeight())}, format.interleaved_format)}; + + nvcv::TensorDataStridedCuda input_data{ + nvcv::TensorShape{input_reqs.shape, input_reqs.rank, input_reqs.layout}, + nvcv::DataType{input_reqs.dtype}, input_buffer}; + + nvcv::Tensor input_tensor{nvcv::TensorWrapData(input_data)}; + + // Allocate the memory buffer ourselves rather than letting CV-CUDA allocate it + float * raw_output_buffer{nullptr}; + const size_t output_buffer_size{img_width * img_height * img_channels * sizeof(float)}; + CheckCudaErrors( + cudaMallocAsync(&raw_output_buffer, output_buffer_size, stream_), __FILE__, __LINE__); + + nvcv::TensorDataStridedCuda::Buffer output_buffer; + output_buffer.strides[3] = sizeof(float); + output_buffer.strides[2] = img_channels * output_buffer.strides[3]; + output_buffer.strides[1] = img_msg.GetWidth() * output_buffer.strides[2]; + output_buffer.strides[0] = img_msg.GetHeight() * output_buffer.strides[1]; + + output_buffer.basePtr = reinterpret_cast(raw_output_buffer); + + nvcv::Tensor::Requirements output_reqs{nvcv::Tensor::CalcRequirements( + kBatchSize, {static_cast(img_msg.GetWidth()), + static_cast(img_msg.GetHeight())}, format.interleaved_float_format)}; + + nvcv::TensorDataStridedCuda output_data{ + nvcv::TensorShape{output_reqs.shape, output_reqs.rank, output_reqs.layout}, + nvcv::DataType{output_reqs.dtype}, output_buffer}; + nvcv::Tensor output_tensor{nvcv::TensorWrapData(output_data)}; + + const float scale_factor = scale_ ? 1.0f / 255.0f : 1.0f; + convert_op_(stream_, input_tensor, output_tensor, scale_factor, 0.0f); + + CheckCudaErrors(cudaStreamSynchronize(stream_), __FILE__, __LINE__); + + // Copy the header information. + std_msgs::msg::Header header; + header.frame_id = img_msg.GetFrameId(); + header.stamp.sec = img_msg.GetTimestampSeconds(); + header.stamp.nanosec = img_msg.GetTimestampNanoseconds(); + + nvidia::isaac_ros::nitros::NitrosTensorList tensor_list = + nvidia::isaac_ros::nitros::NitrosTensorListBuilder() + .WithHeader(header) + .AddTensor( + tensor_name_, (nvidia::isaac_ros::nitros::NitrosTensorBuilder() + .WithShape( + {static_cast(img_msg.GetHeight()), static_cast(img_msg.GetWidth()), + img_channels}) + .WithDataType(nvidia::isaac_ros::nitros::NitrosDataType::kFloat32) + .WithData(raw_output_buffer) + .Build())) + .Build(); + + nitros_tensor_pub_->publish(tensor_list); +} + +ImageToTensorNode::~ImageToTensorNode() +{ + CheckCudaErrors(cudaStreamDestroy(stream_), __FILE__, __LINE__); +} + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#include "rclcpp_components/register_node_macro.hpp" +RCLCPP_COMPONENTS_REGISTER_NODE(nvidia::isaac_ros::dnn_inference::ImageToTensorNode) diff --git a/isaac_ros_tensor_proc/src/interleaved_to_planar_node.cpp b/isaac_ros_tensor_proc/src/interleaved_to_planar_node.cpp new file mode 100644 index 0000000..381190d --- /dev/null +++ b/isaac_ros_tensor_proc/src/interleaved_to_planar_node.cpp @@ -0,0 +1,151 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "isaac_ros_tensor_proc/interleaved_to_planar_node.hpp" + +#include +#include +#include +#include +#include + +#include "isaac_ros_common/qos.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ +namespace +{ + +constexpr char INPUT_COMPONENT_KEY[] = "interleaved_to_planar/data_receiver"; +constexpr char INPUT_DEFAULT_IMAGE_FORMAT[] = "nitros_tensor_list_nhwc_rgb_f32"; +constexpr char INPUT_TOPIC_NAME[] = "interleaved_tensor"; + +constexpr char OUTPUT_COMPONENT_KEY[] = "sink/sink"; +constexpr char OUTPUT_DEFAULT_TENSOR_FORMAT[] = + "nitros_tensor_list_nchw_rgb_f32"; +constexpr char OUTPUT_TOPIC_NAME[] = "planar_tensor"; + +constexpr char APP_YAML_FILENAME[] = "config/interleaved_to_planar_node.yaml"; +constexpr char PACKAGE_NAME[] = "isaac_ros_tensor_proc"; + +const std::vector> EXTENSIONS = { + {"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"}, + {"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"}, + {"isaac_ros_gxf", "gxf/lib/multimedia/libgxf_multimedia.so"}, + {"gxf_isaac_tensorops", "gxf/lib/libgxf_isaac_tensorops.so"}, +}; + +const std::vector PRESET_EXTENSION_SPEC_NAMES = { + "isaac_ros_dnn_encoders", +}; +const std::vector EXTENSION_SPEC_FILENAMES = {}; +const std::vector GENERATOR_RULE_FILENAMES = { + "config/namespace_injector_rule_interleaved_to_planar.yaml"}; + +const std::map +COMPATIBLE_DATA_FORMAT_MAP = { + {INPUT_COMPONENT_KEY, INPUT_DEFAULT_IMAGE_FORMAT}, + {OUTPUT_COMPONENT_KEY, OUTPUT_DEFAULT_TENSOR_FORMAT} +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +const nitros::NitrosPublisherSubscriberConfigMap CONFIG_MAP = { + {INPUT_COMPONENT_KEY, + { + .type = nitros::NitrosPublisherSubscriberType::NEGOTIATED, + .qos = rclcpp::QoS(10), + .compatible_data_format = INPUT_DEFAULT_IMAGE_FORMAT, + .topic_name = INPUT_TOPIC_NAME, + .use_compatible_format_only = false, + }}, + {OUTPUT_COMPONENT_KEY, + {.type = nitros::NitrosPublisherSubscriberType::NEGOTIATED, + .qos = rclcpp::QoS(10), + .compatible_data_format = OUTPUT_DEFAULT_TENSOR_FORMAT, + .topic_name = OUTPUT_TOPIC_NAME, + .use_compatible_format_only = false, + .frame_id_source_key = INPUT_COMPONENT_KEY}}}; +} // namespace +#pragma GCC diagnostic pop + +InterleavedToPlanarNode::InterleavedToPlanarNode(const rclcpp::NodeOptions & options) +: nitros::NitrosNode(options, APP_YAML_FILENAME, CONFIG_MAP, + PRESET_EXTENSION_SPEC_NAMES, EXTENSION_SPEC_FILENAMES, + GENERATOR_RULE_FILENAMES, EXTENSIONS, PACKAGE_NAME), + input_tensor_shape_(declare_parameter>( + "input_tensor_shape", + std::vector())), + num_blocks_(declare_parameter("num_blocks", 40)), + output_tensor_name_(declare_parameter("output_tensor_name", "input_tensor")) +{ + rclcpp::QoS input_qos = ::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "input_qos"); + rclcpp::QoS output_qos = ::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "output_qos"); + for (auto & config : config_map_) { + if (config.second.topic_name == INPUT_TOPIC_NAME) { + config.second.qos = input_qos; + } else { + config.second.qos = output_qos; + } + } + + if (input_tensor_shape_.empty()) { + throw std::invalid_argument("[InterleavedToPlanarNode] The input shape is empty!"); + } + + registerSupportedType(); + startNitrosNode(); +} + +void InterleavedToPlanarNode::postLoadGraphCallback() +{ + RCLCPP_INFO(get_logger(), "In InterleavedToPlanarNode postLoadGraphCallback()."); + + int64_t block_size{std::accumulate( + input_tensor_shape_.begin(), input_tensor_shape_.end(), 1, std::multiplies())}; + if (block_size <= 0) { + throw std::invalid_argument( + "Calculated block size is less than equal to zero! Double check the input tensor shape." + ); + } + + getNitrosContext().setParameterUInt64( + "interleaved_to_planar", + "nvidia::gxf::BlockMemoryPool", + "block_size", block_size * sizeof(float)); + + getNitrosContext().setParameterUInt64( + "interleaved_to_planar", + "nvidia::gxf::BlockMemoryPool", + "num_blocks", num_blocks_); + + getNitrosContext().setParameterStr( + "interleaved_to_planar", "nvidia::isaac::tensor_ops::InterleavedToPlanar", "output_name", + output_tensor_name_); +} + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#include "rclcpp_components/register_node_macro.hpp" +RCLCPP_COMPONENTS_REGISTER_NODE(nvidia::isaac_ros::dnn_inference::InterleavedToPlanarNode) diff --git a/isaac_ros_tensor_proc/src/normalize_node.cpp b/isaac_ros_tensor_proc/src/normalize_node.cpp new file mode 100644 index 0000000..9317a3f --- /dev/null +++ b/isaac_ros_tensor_proc/src/normalize_node.cpp @@ -0,0 +1,215 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "isaac_ros_tensor_proc/normalize_node.hpp" + +#include +#include +#include +#include + +#include "isaac_ros_common/qos.hpp" +#include "isaac_ros_nitros_image_type/nitros_image.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ +namespace +{ + +// Map to store the nitros tensor format to nitros image type +const std::unordered_map tensor_to_image_type( + {{"nitros_tensor_list_nhwc_rgb_f32", "nitros_image_rgb8"}, + {"nitros_tensor_list_nhwc_bgr_f32", "nitros_image_bgr8"}}); + +constexpr double PER_PIXEL_SCALE = 255.0; + +constexpr char INPUT_COMPONENT_KEY[] = "normalizer/data_receiver"; +constexpr char INPUT_DEFAULT_IMAGE_FORMAT[] = "nitros_image_rgb8"; +constexpr char INPUT_TOPIC_NAME[] = "image"; + +constexpr char OUTPUT_COMPONENT_KEY[] = "sink/sink"; +constexpr char OUTPUT_DEFAULT_TENSOR_FORMAT[] = + "nitros_tensor_list_nhwc_rgb_f32"; +constexpr char OUTPUT_TOPIC_NAME[] = "normalized_tensor"; + +constexpr char APP_YAML_FILENAME[] = "config/normalizer_node.yaml"; +constexpr char PACKAGE_NAME[] = "isaac_ros_tensor_proc"; + +const std::vector> EXTENSIONS = { + {"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"}, + {"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"}, + {"gxf_isaac_tensorops", "gxf/lib/libgxf_isaac_tensorops.so"}, +}; + +const std::vector PRESET_EXTENSION_SPEC_NAMES = { + "isaac_ros_dnn_encoders", +}; +const std::vector EXTENSION_SPEC_FILENAMES = {}; +const std::vector GENERATOR_RULE_FILENAMES = { + "config/namespace_injector_rule_normalizer.yaml"}; + +const std::map +COMPATIBLE_DATA_FORMAT_MAP = { + {INPUT_COMPONENT_KEY, INPUT_DEFAULT_IMAGE_FORMAT}, + {OUTPUT_COMPONENT_KEY, OUTPUT_DEFAULT_TENSOR_FORMAT} +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +const nitros::NitrosPublisherSubscriberConfigMap CONFIG_MAP = { + {INPUT_COMPONENT_KEY, + { + .type = nitros::NitrosPublisherSubscriberType::NEGOTIATED, + .qos = rclcpp::QoS(10), + .compatible_data_format = INPUT_DEFAULT_IMAGE_FORMAT, + .topic_name = INPUT_TOPIC_NAME, + .use_compatible_format_only = false, + }}, + {OUTPUT_COMPONENT_KEY, + {.type = nitros::NitrosPublisherSubscriberType::NEGOTIATED, + .qos = rclcpp::QoS(10), + .compatible_data_format = OUTPUT_DEFAULT_TENSOR_FORMAT, + .topic_name = OUTPUT_TOPIC_NAME, + .use_compatible_format_only = false, + .frame_id_source_key = INPUT_COMPONENT_KEY}}}; +} // namespace +#pragma GCC diagnostic pop + +NormalizeNode::NormalizeNode(const rclcpp::NodeOptions & options) +: nitros::NitrosNode(options, APP_YAML_FILENAME, CONFIG_MAP, + PRESET_EXTENSION_SPEC_NAMES, EXTENSION_SPEC_FILENAMES, + GENERATOR_RULE_FILENAMES, EXTENSIONS, PACKAGE_NAME), + image_mean_(declare_parameter>( + "image_mean", + {0.5, 0.5, 0.5})), + image_stddev_(declare_parameter>( + "image_stddev", + {0.5, 0.5, 0.5})), + input_image_width_(declare_parameter("input_image_width", 0)), + input_image_height_(declare_parameter("input_image_height", 0)), + num_blocks_(declare_parameter("num_blocks", 40)), + output_tensor_name_(declare_parameter("output_tensor_name", "image")) +{ + rclcpp::QoS input_qos = ::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "input_qos"); + rclcpp::QoS output_qos = ::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "output_qos"); + for (auto & config : config_map_) { + if (config.second.topic_name == INPUT_TOPIC_NAME) { + config.second.qos = input_qos; + } else { + config.second.qos = output_qos; + } + } + + if (input_image_width_ == 0) { + throw std::invalid_argument( + "[NormalizeNode] Invalid input_image_width"); + } + if (input_image_height_ == 0) { + throw std::invalid_argument( + "[NormalizeNode] Invalid input_image_height"); + } + + if (image_mean_.size() != 3 || image_stddev_.size() != 3) { + throw std::invalid_argument( + "[NormalizeNode] Did not receive 3 image mean channels or 3 image stddev channels"); + } + + registerSupportedType(); + registerSupportedType(); + startNitrosNode(); +} + +void NormalizeNode::preLoadGraphCallback() +{ + RCLCPP_INFO( + get_logger(), + "In NormalizeNode Node preLoadGraphCallback()."); + + std::vector scale = {1.0 / (PER_PIXEL_SCALE * image_stddev_[0]), + 1.0 / (PER_PIXEL_SCALE * image_stddev_[1]), + 1.0 / (PER_PIXEL_SCALE * image_stddev_[2])}; + std::vector offset = {-(PER_PIXEL_SCALE * image_mean_[0]), + -(PER_PIXEL_SCALE * image_mean_[1]), + -(PER_PIXEL_SCALE * image_mean_[2])}; + + std::string scales = "[" + std::to_string(scale[0]) + "," + + std::to_string(scale[1]) + "," + + std::to_string(scale[2]) + "]"; + NitrosNode::preLoadGraphSetParameter( + "normalizer", "nvidia::isaac::tensor_ops::Normalize", "scales", scales); + + std::string offsets = "[" + std::to_string(offset[0]) + "," + + std::to_string(offset[1]) + "," + + std::to_string(offset[2]) + "]"; + NitrosNode::preLoadGraphSetParameter( + "normalizer", + "nvidia::isaac::tensor_ops::Normalize", + "offsets", offsets); +} + +void NormalizeNode::postLoadGraphCallback() +{ + RCLCPP_INFO(get_logger(), "In Normalizer Node postLoadGraphCallback()."); + + const gxf::optimizer::ComponentInfo output_comp_info = { + "nvidia::isaac_ros::MessageRelay", // component_type_name + "sink", // component_name + "sink" // entity_name + }; + const std::string negotiated_tensor_format = + getFinalDataFormat(output_comp_info); + const auto image_type = tensor_to_image_type.find(negotiated_tensor_format); + if (image_type == std::end(tensor_to_image_type)) { + RCLCPP_ERROR( + get_logger(), "Unsupported NITROS tensor type[%s].", + negotiated_tensor_format.c_str()); + throw std::runtime_error("Unsupported NITROS tensor type."); + } + + uint64_t block_size = calculate_image_size( + image_type->second, input_image_width_, input_image_height_); + + RCLCPP_DEBUG( + get_logger(), "postLoadGraphCallback() block_size = %ld.", + block_size); + + getNitrosContext().setParameterUInt64( + "normalizer", + "nvidia::gxf::BlockMemoryPool", + "block_size", block_size * sizeof(float)); + + getNitrosContext().setParameterStr( + "normalizer", "nvidia::isaac::tensor_ops::Normalize", "output_name", + output_tensor_name_); + + getNitrosContext().setParameterUInt64( + "normalizer", + "nvidia::gxf::BlockMemoryPool", + "num_blocks", num_blocks_); +} + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#include "rclcpp_components/register_node_macro.hpp" +RCLCPP_COMPONENTS_REGISTER_NODE(nvidia::isaac_ros::dnn_inference::NormalizeNode) diff --git a/isaac_ros_tensor_proc/src/reshape_node.cpp b/isaac_ros_tensor_proc/src/reshape_node.cpp new file mode 100644 index 0000000..65522fe --- /dev/null +++ b/isaac_ros_tensor_proc/src/reshape_node.cpp @@ -0,0 +1,183 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include "isaac_ros_tensor_proc/reshape_node.hpp" + +#include +#include +#include +#include +#include + +#include "isaac_ros_common/qos.hpp" +#include "isaac_ros_nitros_tensor_list_type/nitros_tensor_list.hpp" + +namespace nvidia +{ +namespace isaac_ros +{ +namespace dnn_inference +{ +namespace +{ + +constexpr char INPUT_COMPONENT_KEY[] = "reshaper/data_receiver"; +constexpr char INPUT_DEFAULT_IMAGE_FORMAT[] = "nitros_tensor_list_nchw_rgb_f32"; +constexpr char INPUT_TOPIC_NAME[] = "tensor"; + +constexpr char OUTPUT_COMPONENT_KEY[] = "sink/sink"; +constexpr char OUTPUT_DEFAULT_TENSOR_FORMAT[] = + "nitros_tensor_list_nchw_rgb_f32"; +constexpr char OUTPUT_TOPIC_NAME[] = "reshaped_tensor"; + +constexpr char APP_YAML_FILENAME[] = "config/reshape_node.yaml"; +constexpr char PACKAGE_NAME[] = "isaac_ros_tensor_proc"; + +const std::vector> EXTENSIONS = { + {"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"}, + {"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"}, + {"isaac_ros_gxf", "gxf/lib/multimedia/libgxf_multimedia.so"}, + {"gxf_isaac_tensorops", "gxf/lib/libgxf_isaac_tensorops.so"}, +}; + +const std::vector PRESET_EXTENSION_SPEC_NAMES = { + "isaac_ros_dnn_encoders", +}; +const std::vector EXTENSION_SPEC_FILENAMES = {}; +const std::vector GENERATOR_RULE_FILENAMES = { + "config/namespace_injector_rule_reshaper.yaml"}; + +const std::map +COMPATIBLE_DATA_FORMAT_MAP = { + {INPUT_COMPONENT_KEY, INPUT_DEFAULT_IMAGE_FORMAT}, + {OUTPUT_COMPONENT_KEY, OUTPUT_DEFAULT_TENSOR_FORMAT} +}; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +const nitros::NitrosPublisherSubscriberConfigMap CONFIG_MAP = { + {INPUT_COMPONENT_KEY, + { + .type = nitros::NitrosPublisherSubscriberType::NEGOTIATED, + .qos = rclcpp::QoS(10), + .compatible_data_format = INPUT_DEFAULT_IMAGE_FORMAT, + .topic_name = INPUT_TOPIC_NAME, + .use_compatible_format_only = false, + }}, + {OUTPUT_COMPONENT_KEY, + {.type = nitros::NitrosPublisherSubscriberType::NEGOTIATED, + .qos = rclcpp::QoS(10), + .compatible_data_format = OUTPUT_DEFAULT_TENSOR_FORMAT, + .topic_name = OUTPUT_TOPIC_NAME, + .use_compatible_format_only = false, + .frame_id_source_key = INPUT_COMPONENT_KEY}}}; +} // namespace +#pragma GCC diagnostic pop + +ReshapeNode::ReshapeNode(const rclcpp::NodeOptions & options) +: nitros::NitrosNode(options, APP_YAML_FILENAME, CONFIG_MAP, + PRESET_EXTENSION_SPEC_NAMES, EXTENSION_SPEC_FILENAMES, + GENERATOR_RULE_FILENAMES, EXTENSIONS, PACKAGE_NAME), + output_tensor_name_(declare_parameter("output_tensor_name", "input_tensor")), + input_tensor_shape_(declare_parameter>( + "input_tensor_shape", + std::vector())), + output_tensor_shape_(declare_parameter>( + "output_tensor_shape", + std::vector())), + num_blocks_(declare_parameter("num_blocks", 40)) +{ + rclcpp::QoS input_qos = ::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "input_qos"); + rclcpp::QoS output_qos = ::isaac_ros::common::AddQosParameter(*this, "DEFAULT", "output_qos"); + for (auto & config : config_map_) { + if (config.second.topic_name == INPUT_TOPIC_NAME) { + config.second.qos = input_qos; + } else { + config.second.qos = output_qos; + } + } + + if (input_tensor_shape_.empty() || output_tensor_shape_.empty()) { + throw std::invalid_argument("[ReshapeNode] The input or output tensor shape is empty!"); + } + + const int64_t input_element_count{std::accumulate( + input_tensor_shape_.begin(), + input_tensor_shape_.end(), + 1, + std::multiplies())}; + + const int64_t output_element_count{std::accumulate( + output_tensor_shape_.begin(), + output_tensor_shape_.end(), + 1, + std::multiplies())}; + + if (input_element_count != output_element_count) { + throw std::invalid_argument( + "[ReshapeNode] The input element count and output element count do not match!"); + } + + registerSupportedType(); + startNitrosNode(); +} + +void ReshapeNode::postLoadGraphCallback() +{ + RCLCPP_INFO(get_logger(), "In ReshapeNode postLoadGraphCallback()."); + + int64_t block_size{std::accumulate( + input_tensor_shape_.begin(), input_tensor_shape_.end(), 1, std::multiplies())}; + if (block_size <= 0) { + throw std::invalid_argument( + "Calculated block size is less than equal to zero! Double check the input tensor shape." + ); + } + + getNitrosContext().setParameterUInt64( + "reshaper", + "nvidia::gxf::BlockMemoryPool", + "block_size", block_size * sizeof(float)); + + getNitrosContext().setParameterUInt64( + "reshaper", + "nvidia::gxf::BlockMemoryPool", + "num_blocks", num_blocks_); + std::vector final_shape; + std::transform( + output_tensor_shape_.begin(), + output_tensor_shape_.end(), + std::back_inserter(final_shape), + [](const int64_t value) { + return static_cast(value); + } + ); + getNitrosContext().setParameter1DInt32Vector( + "reshaper", "nvidia::isaac::tensor_ops::Reshape", "output_shape", + final_shape); + + getNitrosContext().setParameterStr( + "reshaper", "nvidia::isaac::tensor_ops::Reshape", "output_name", + output_tensor_name_); +} + +} // namespace dnn_inference +} // namespace isaac_ros +} // namespace nvidia + +#include "rclcpp_components/register_node_macro.hpp" +RCLCPP_COMPONENTS_REGISTER_NODE(nvidia::isaac_ros::dnn_inference::ReshapeNode) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_image_tensor_normalize_test.py b/isaac_ros_tensor_proc/test/isaac_ros_image_tensor_normalize_test.py new file mode 100644 index 0000000..244f82a --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_image_tensor_normalize_test.py @@ -0,0 +1,146 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from isaac_ros_tensor_list_interfaces.msg import Tensor, TensorList, TensorShape +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + + +DIMENSION_WIDTH = 100 +DIMENSION_HEIGHT = 100 +DIMENSION_CHANNELS = 3 + + +@pytest.mark.rostest +def generate_test_description(): + normalize_node = ComposableNode( + name='normalize_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ImageTensorNormalizeNode', + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + parameters=[{ + 'mean': [0.5, 0.6, 0.25], + 'stddev': [0.25, 0.8, 0.5], + }], + remappings=[('normalized_tensor', 'tensors')]) + + return IsaacROSNormalizeNodeTest.generate_test_description([ + ComposableNodeContainer( + name='normalize_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[normalize_node], + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + output='screen', + arguments=['--ros-args', '--log-level', 'info', + '--log-level', 'isaac_ros_test.encoder:=debug'], + ) + ]) + + +class IsaacROSNormalizeNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_image_normalization(self): + """ + Test Image Normalization feature. + + Test that the NormalizeNode is correctly normalizing the image based on + the given image mean and standard deviation vectors. + Given that the image mean vector is <0.5, 0.6, 0.25>, and the image standard + deviation vector is <0.25, 0.8, 0.5>, and that our input image is white + (each pixel value is 255), the value for each channel should be: + RED: ((255 / 255) - 0.5) / 0.25 = 2.0 + GREEN: ((255 / 255) - 0.6) / 0.8 = 0.5 + BLUE: ((255/ 255) - 0.25) / 0.5 = 1.5 + This test verifies that each channel's values should be the calculated values + above. + """ + TIMEOUT = 300 + received_messages = {} + RED_EXPECTED_VAL = 2.0 + GREEN_EXPECTED_VAL = 0.5 + BLUE_EXPECTED_VAL = 1.5 + EXPECTED_VALS = [RED_EXPECTED_VAL, GREEN_EXPECTED_VAL, + BLUE_EXPECTED_VAL] + + TENSOR_NAME = 'tensor' + TENSOR_DATA_TYPE = 9 + TENSOR_DIMENSIONS = [DIMENSION_HEIGHT, DIMENSION_WIDTH, DIMENSION_CHANNELS] + TENSOR_RANK = len(TENSOR_DIMENSIONS) + + self.generate_namespace_lookup(['tensor', 'image', 'tensors']) + + image_pub = self.node.create_publisher( + TensorList, self.namespaces['tensor'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + tensor_list = TensorList() + tensor = Tensor() + shape = TensorShape() + + shape.rank = TENSOR_RANK + shape.dims = TENSOR_DIMENSIONS + tensor.shape = shape + + tensor.name = TENSOR_NAME + tensor.data_type = TENSOR_DATA_TYPE + # NOTE: we let NITROS handle stride calculation, etc + tensor.strides = [] + tensor_data = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, + DIMENSION_CHANNELS), np.float32) + tensor_data[:] = 1.0 + tensor.data = tensor_data.tobytes() + tensor_list.tensors = [tensor] + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + image_pub.publish(tensor_list) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + tensor = received_messages['tensors'].tensors[0] + + # The tensor has the format HWC and is a float array, so + # use numpy to interpret it as such, and then reshape it + normalized_tensor = np.frombuffer(tensor.data, np.float32) + normalized_tensor = normalized_tensor.reshape(DIMENSION_HEIGHT, + DIMENSION_WIDTH, + DIMENSION_CHANNELS) + for c in range(DIMENSION_CHANNELS): + self.assertTrue( + (np.round(normalized_tensor[:, :, c], 1) == EXPECTED_VALS[c]).all() + ) + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(image_pub) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor.py b/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor.py new file mode 100644 index 0000000..3140060 --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor.py @@ -0,0 +1,126 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from cv_bridge import CvBridge +from isaac_ros_tensor_list_interfaces.msg import TensorList +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + +from sensor_msgs.msg import Image + + +DIMENSION_WIDTH = 100 +DIMENSION_HEIGHT = 100 + + +@pytest.mark.rostest +def generate_test_description(): + normalize_node = ComposableNode( + name='image_to_tensor_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ImageToTensorNode', + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + parameters=[{ + 'scale': True, + 'tensor_name': 'tensor', + }], + remappings=[('tensor', 'tensors')]) + + return IsaacROSNormalizeNodeTest.generate_test_description([ + ComposableNodeContainer( + name='normalize_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[normalize_node], + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + output='screen', + ) + ]) + + +class IsaacROSNormalizeNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_image_normalization(self): + """ + Test Image Normalization feature. + + Test that the NormalizeNode is correctly normalizing the image based on + the given image mean and standard deviation vectors. + Given that the image mean vector is <0.5, 0.6, 0.25>, and the image standard + deviation vector is <0.25, 0.8, 0.5>, and that our input image is white + (each pixel value is 255), the value for each channel should be: + RED: ((255 / 255) - 0.5) / 0.25 = 2.0 + GREEN: ((255 / 255) - 0.6) / 0.8 = 0.5 + BLUE: ((255/ 255) - 0.25) / 0.5 = 1.5 + This test verifies that each channel's values should be the calculated values + above. + """ + TIMEOUT = 300 + received_messages = {} + EXPECTED_VALS = [1.0, 0.5, 0.2] + + self.generate_namespace_lookup(['image', 'tensors']) + + image_pub = self.node.create_publisher( + Image, self.namespaces['image'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + # Create white image + cv_image = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, 3), np.uint8) + cv_image[:] = (255, 127, 51) + image = CvBridge().cv2_to_imgmsg(cv_image) + image.encoding = 'bgr8' + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + image_pub.publish(image) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + tensor = received_messages['tensors'].tensors[0] + + # The tensor has the format HWC and is a float array, so + # use numpy to interpret it as such, and then reshape it + DIMENSION_CHANNEL = 3 + normalized_tensor = np.frombuffer(tensor.data, np.float32) + normalized_tensor = normalized_tensor.reshape(DIMENSION_HEIGHT, + DIMENSION_WIDTH, + DIMENSION_CHANNEL) + for c in range(DIMENSION_CHANNEL): + self.assertTrue( + (np.round(normalized_tensor[:, :, c], 1) == EXPECTED_VALS[c]).all() + ) + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(image_pub) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale.py b/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale.py new file mode 100644 index 0000000..02cfadf --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale.py @@ -0,0 +1,126 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from cv_bridge import CvBridge +from isaac_ros_tensor_list_interfaces.msg import TensorList +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + +from sensor_msgs.msg import Image + + +DIMENSION_WIDTH = 100 +DIMENSION_HEIGHT = 100 + + +@pytest.mark.rostest +def generate_test_description(): + normalize_node = ComposableNode( + name='image_to_tensor_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ImageToTensorNode', + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + parameters=[{ + 'scale': False, + 'tensor_name': 'tensor', + }], + remappings=[('tensor', 'tensors')]) + + return IsaacROSNormalizeNodeTest.generate_test_description([ + ComposableNodeContainer( + name='normalize_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[normalize_node], + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + output='screen', + ) + ]) + + +class IsaacROSNormalizeNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_image_normalization(self): + """ + Test Image Normalization feature. + + Test that the NormalizeNode is correctly normalizing the image based on + the given image mean and standard deviation vectors. + Given that the image mean vector is <0.5, 0.6, 0.25>, and the image standard + deviation vector is <0.25, 0.8, 0.5>, and that our input image is white + (each pixel value is 255), the value for each channel should be: + RED: ((255 / 255) - 0.5) / 0.25 = 2.0 + GREEN: ((255 / 255) - 0.6) / 0.8 = 0.5 + BLUE: ((255/ 255) - 0.25) / 0.5 = 1.5 + This test verifies that each channel's values should be the calculated values + above. + """ + TIMEOUT = 300 + received_messages = {} + EXPECTED_VALS = [127] + + self.generate_namespace_lookup(['image', 'tensors']) + + image_pub = self.node.create_publisher( + Image, self.namespaces['image'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + # Create white image + cv_image = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, 1), np.uint8) + cv_image[:] = EXPECTED_VALS + image = CvBridge().cv2_to_imgmsg(cv_image) + image.encoding = 'mono8' + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + image_pub.publish(image) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + tensor = received_messages['tensors'].tensors[0] + + # The tensor has the format HWC and is a float array, so + # use numpy to interpret it as such, and then reshape it + DIMENSION_CHANNEL = 1 + normalized_tensor = np.frombuffer(tensor.data, np.float32) + normalized_tensor = normalized_tensor.reshape(DIMENSION_HEIGHT, + DIMENSION_WIDTH, + DIMENSION_CHANNEL) + for c in range(DIMENSION_CHANNEL): + self.assertTrue( + (np.round(normalized_tensor[:, :, c], 1) == EXPECTED_VALS[c]).all() + ) + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(image_pub) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale_float.py b/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale_float.py new file mode 100644 index 0000000..27d1dbc --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_image_to_tensor_no_scale_float.py @@ -0,0 +1,125 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from cv_bridge import CvBridge +from isaac_ros_tensor_list_interfaces.msg import TensorList +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + +from sensor_msgs.msg import Image + + +DIMENSION_WIDTH = 100 +DIMENSION_HEIGHT = 100 + + +@pytest.mark.rostest +def generate_test_description(): + normalize_node = ComposableNode( + name='image_to_tensor_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ImageToTensorNode', + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + parameters=[{ + 'scale': False, + 'tensor_name': 'tensor', + }], + remappings=[('tensor', 'tensors')]) + + return IsaacROSNormalizeNodeTest.generate_test_description([ + ComposableNodeContainer( + name='normalize_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[normalize_node], + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + output='screen', + ) + ]) + + +class IsaacROSNormalizeNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_image_normalization(self): + """ + Test Image Normalization feature. + + Test that the NormalizeNode is correctly normalizing the image based on + the given image mean and standard deviation vectors. + Given that the image mean vector is <0.5, 0.6, 0.25>, and the image standard + deviation vector is <0.25, 0.8, 0.5>, and that our input image is white + (each pixel value is 255), the value for each channel should be: + RED: ((255 / 255) - 0.5) / 0.25 = 2.0 + GREEN: ((255 / 255) - 0.6) / 0.8 = 0.5 + BLUE: ((255/ 255) - 0.25) / 0.5 = 1.5 + This test verifies that each channel's values should be the calculated values + above. + """ + TIMEOUT = 300 + received_messages = {} + EXPECTED_VALS = [1.0, 125.5, 0.2, 1.3] + + self.generate_namespace_lookup(['image', 'tensors']) + + image_pub = self.node.create_publisher( + Image, self.namespaces['image'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + # Create white image + cv_image = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, 4), np.float32) + cv_image[:] = EXPECTED_VALS + image = CvBridge().cv2_to_imgmsg(cv_image, '32FC4') + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + image_pub.publish(image) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + tensor = received_messages['tensors'].tensors[0] + + # The tensor has the format HWC and is a float array, so + # use numpy to interpret it as such, and then reshape it + DIMENSION_CHANNEL = 4 + normalized_tensor = np.frombuffer(tensor.data, np.float32) + normalized_tensor = normalized_tensor.reshape(DIMENSION_HEIGHT, + DIMENSION_WIDTH, + DIMENSION_CHANNEL) + for c in range(DIMENSION_CHANNEL): + self.assertTrue( + (np.round(normalized_tensor[:, :, c], 1) == EXPECTED_VALS[c]).all() + ) + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(image_pub) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_interleaved_to_planar_test.py b/isaac_ros_tensor_proc/test/isaac_ros_interleaved_to_planar_test.py new file mode 100644 index 0000000..7c8f957 --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_interleaved_to_planar_test.py @@ -0,0 +1,146 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from isaac_ros_tensor_list_interfaces.msg import Tensor, TensorList, TensorShape +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + + +DIMENSION_WIDTH = 75 +DIMENSION_HEIGHT = 100 +DIMENSION_CHANNELS = 3 + + +@pytest.mark.rostest +def generate_test_description(): + interleave_to_planar_node = ComposableNode( + name='interleave_to_planar_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::InterleavedToPlanarNode', + namespace=IsaacROSInterleavedToPlanarNodeTest.generate_namespace(), + parameters=[{ + 'input_tensor_shape': [DIMENSION_HEIGHT, DIMENSION_WIDTH, DIMENSION_CHANNELS], + 'output_tensor_name': 'planar_tensor' + }], + remappings=[ + ('planar_tensor', 'tensors') + ]) + + return IsaacROSInterleavedToPlanarNodeTest.generate_test_description([ + ComposableNodeContainer( + name='interleaved_to_planar_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[interleave_to_planar_node], + namespace=IsaacROSInterleavedToPlanarNodeTest.generate_namespace(), + output='screen', + arguments=['--ros-args', '--log-level', 'info', + '--log-level', 'isaac_ros_test.encoder:=debug'], + ) + ]) + + +class IsaacROSInterleavedToPlanarNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_interleaved_to_planar(self): + """ + Test Tensor Interleaved To Planar feature. + + This test simply takes a HWC tensor (100x75x3). The first channel is + filled with 0, the second channel with 1 and the last channel with 2. + Afterwards, it tests that the ordered CHW tensor (3x100x75) has the + expected properties and data. + """ + TIMEOUT = 300 + + TENSOR_NAME = 'input_tensor' + TENSOR_DATA_TYPE = 9 + TENSOR_DIMENSIONS = [DIMENSION_HEIGHT, DIMENSION_WIDTH, DIMENSION_CHANNELS] + TENSOR_RANK = len(TENSOR_DIMENSIONS) + + received_messages = {} + + self.generate_namespace_lookup(['interleaved_tensor', 'tensors']) + + tensor_pub = self.node.create_publisher( + TensorList, self.namespaces['interleaved_tensor'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + # Create tensor + tensor_list = TensorList() + tensor = Tensor() + shape = TensorShape() + + shape.rank = TENSOR_RANK + shape.dims = TENSOR_DIMENSIONS + tensor.shape = shape + + tensor.name = TENSOR_NAME + tensor.data_type = TENSOR_DATA_TYPE + # NOTE: we let NITROS handle stride calculation, etc + tensor.strides = [] + tensor_data = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, + DIMENSION_CHANNELS), np.float32) + tensor_data[:] = list(range(DIMENSION_CHANNELS)) + tensor.data = tensor_data.tobytes() + tensor_list.tensors = [tensor] + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + tensor_pub.publish(tensor_list) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + result_tensor_list = received_messages['tensors'] + + self.assertEqual(len(result_tensor_list.tensors), 1) + result_tensor = result_tensor_list.tensors[0] + self.assertEqual(result_tensor.shape.rank, TENSOR_RANK) + self.assertEqual(result_tensor.name, 'planar_tensor') + + RESULTANT_DIMS = [DIMENSION_CHANNELS, DIMENSION_HEIGHT, + DIMENSION_WIDTH] + + self.assertEqual(result_tensor.shape.dims.tolist(), + RESULTANT_DIMS) + self.assertTrue(result_tensor.data_type == TENSOR_DATA_TYPE) + + resultant_data = np.frombuffer(result_tensor.data, np.float32) + resultant_data = np.reshape(resultant_data, tuple(RESULTANT_DIMS)) + for i in range(DIMENSION_CHANNELS): + self.assertTrue((resultant_data[i, :, :] == i).all()) + + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(tensor_pub) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_normalize_test.py b/isaac_ros_tensor_proc/test/isaac_ros_normalize_test.py new file mode 100644 index 0000000..78d1bea --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_normalize_test.py @@ -0,0 +1,136 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from cv_bridge import CvBridge +from isaac_ros_tensor_list_interfaces.msg import TensorList +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + +from sensor_msgs.msg import Image + + +DIMENSION_WIDTH = 100 +DIMENSION_HEIGHT = 100 + + +@pytest.mark.rostest +def generate_test_description(): + normalize_node = ComposableNode( + name='normalize_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::NormalizeNode', + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + parameters=[{ + 'input_image_width': DIMENSION_WIDTH, + 'input_image_height': DIMENSION_HEIGHT, + 'image_mean': [0.5, 0.6, 0.25], + 'image_stddev': [0.25, 0.8, 0.5], + 'output_tensor_name': 'normalize_test' + }], + remappings=[('normalized_tensor', 'tensors')]) + + return IsaacROSNormalizeNodeTest.generate_test_description([ + ComposableNodeContainer( + name='normalize_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[normalize_node], + namespace=IsaacROSNormalizeNodeTest.generate_namespace(), + output='screen', + arguments=['--ros-args', '--log-level', 'info', + '--log-level', 'isaac_ros_test.encoder:=debug'], + ) + ]) + + +class IsaacROSNormalizeNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_image_normalization(self): + """ + Test Image Normalization feature. + + Test that the NormalizeNode is correctly normalizing the image based on + the given image mean and standard deviation vectors. + Given that the image mean vector is <0.5, 0.6, 0.25>, and the image standard + deviation vector is <0.25, 0.8, 0.5>, and that our input image is white + (each pixel value is 255), the value for each channel should be: + RED: ((255 / 255) - 0.5) / 0.25 = 2.0 + GREEN: ((255 / 255) - 0.6) / 0.8 = 0.5 + BLUE: ((255/ 255) - 0.25) / 0.5 = 1.5 + This test verifies that each channel's values should be the calculated values + above. + """ + TIMEOUT = 300 + received_messages = {} + RED_EXPECTED_VAL = 2.0 + GREEN_EXPECTED_VAL = 0.5 + BLUE_EXPECTED_VAL = 1.5 + EXPECTED_VALS = [RED_EXPECTED_VAL, GREEN_EXPECTED_VAL, + BLUE_EXPECTED_VAL] + + self.generate_namespace_lookup(['image', 'tensors']) + + image_pub = self.node.create_publisher( + Image, self.namespaces['image'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + # Create white image + cv_image = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, 3), np.uint8) + cv_image[:] = (255, 255, 255) + image = CvBridge().cv2_to_imgmsg(cv_image) + image.encoding = 'bgr8' + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + image_pub.publish(image) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + tensor = received_messages['tensors'].tensors[0] + self.assertEqual(tensor.name, 'normalize_test') + + # The tensor has the format HWC and is a float array, so + # use numpy to interpret it as such, and then reshape it + DIMENSION_CHANNEL = 3 + normalized_tensor = np.frombuffer(tensor.data, np.float32) + normalized_tensor = normalized_tensor.reshape(DIMENSION_HEIGHT, + DIMENSION_WIDTH, + DIMENSION_CHANNEL) + for c in range(DIMENSION_CHANNEL): + self.assertTrue( + (np.round(normalized_tensor[:, :, c], 1) == EXPECTED_VALS[c]).all() + ) + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(image_pub) diff --git a/isaac_ros_tensor_proc/test/isaac_ros_reshape_test.py b/isaac_ros_tensor_proc/test/isaac_ros_reshape_test.py new file mode 100644 index 0000000..b4fc028 --- /dev/null +++ b/isaac_ros_tensor_proc/test/isaac_ros_reshape_test.py @@ -0,0 +1,148 @@ +# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +# Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +import os +import pathlib +import time + +from isaac_ros_tensor_list_interfaces.msg import Tensor, TensorList, TensorShape +from isaac_ros_test import IsaacROSBaseTest +from launch_ros.actions import ComposableNodeContainer +from launch_ros.descriptions import ComposableNode +import numpy as np + +import pytest +import rclpy + + +DIMENSION_WIDTH = 75 +DIMENSION_HEIGHT = 100 +DIMENSION_CHANNELS = 3 + + +@pytest.mark.rostest +def generate_test_description(): + reshape_node = ComposableNode( + name='reshape_node', + package='isaac_ros_tensor_proc', + plugin='nvidia::isaac_ros::dnn_inference::ReshapeNode', + namespace=IsaacROSDnnReshapeNodeTest.generate_namespace(), + parameters=[{ + 'input_tensor_shape': [DIMENSION_HEIGHT, DIMENSION_WIDTH, + DIMENSION_CHANNELS], + 'output_tensor_shape': [1, DIMENSION_HEIGHT, DIMENSION_WIDTH, + DIMENSION_CHANNELS], + 'output_tensor_name': 'output', + }], + remappings=[ + ('reshaped_tensor', 'tensors') + ]) + + return IsaacROSDnnReshapeNodeTest.generate_test_description([ + ComposableNodeContainer( + name='tensor_rt_container', + package='rclcpp_components', + executable='component_container_mt', + composable_node_descriptions=[reshape_node], + namespace=IsaacROSDnnReshapeNodeTest.generate_namespace(), + output='screen', + arguments=['--ros-args', '--log-level', 'info', + '--log-level', 'isaac_ros_test.encoder:=debug'], + ) + ]) + + +class IsaacROSDnnReshapeNodeTest(IsaacROSBaseTest): + filepath = pathlib.Path(os.path.dirname(__file__)) + + def test_reshape(self): + """ + Test Tensor Reshape feature. + + This test simply takes a HWC tensor (100x75x3). The first channel is + filled with 0, the second channel with 1 and the last channel with 2. + Afterward, it tests the shape of the new tensor with 1x100x75x3 + """ + TIMEOUT = 300 + + TENSOR_NAME = 'input_tensor' + TENSOR_DATA_TYPE = 9 + TENSOR_DIMENSIONS = [DIMENSION_HEIGHT, DIMENSION_WIDTH, DIMENSION_CHANNELS] + TENSOR_RANK = len(TENSOR_DIMENSIONS) + + received_messages = {} + + self.generate_namespace_lookup(['tensor', 'tensors']) + + tensor_pub = self.node.create_publisher( + TensorList, self.namespaces['tensor'], self.DEFAULT_QOS) + + subs = self.create_logging_subscribers( + [('tensors', TensorList)], received_messages) + + try: + # Create tensor + tensor_list = TensorList() + tensor = Tensor() + shape = TensorShape() + + shape.rank = TENSOR_RANK + shape.dims = TENSOR_DIMENSIONS + tensor.shape = shape + + tensor.name = TENSOR_NAME + tensor.data_type = TENSOR_DATA_TYPE + # NOTE: we let NITROS handle stride calculation, etc + tensor.strides = [] + tensor_data = np.zeros((DIMENSION_HEIGHT, DIMENSION_WIDTH, + DIMENSION_CHANNELS), np.float32) + tensor_data[:] = list(range(DIMENSION_CHANNELS)) + tensor.data = tensor_data.tobytes() + tensor_list.tensors = [tensor] + + end_time = time.time() + TIMEOUT + done = False + + while time.time() < end_time: + tensor_pub.publish(tensor_list) + rclpy.spin_once(self.node, timeout_sec=(0.1)) + if 'tensors' in received_messages: + done = True + break + self.assertTrue(done, 'Appropriate output not received') + result_tensor_list = received_messages['tensors'] + + self.assertEqual(len(result_tensor_list.tensors), 1) + result_tensor = result_tensor_list.tensors[0] + self.assertEqual(result_tensor.shape.rank, TENSOR_RANK + 1) + self.assertEqual(result_tensor.name, 'output') + + RESULTANT_DIMS = [1, DIMENSION_HEIGHT, DIMENSION_WIDTH, + DIMENSION_CHANNELS] + + self.assertEqual(result_tensor.shape.dims.tolist(), + RESULTANT_DIMS) + self.assertEqual(result_tensor.data_type, TENSOR_DATA_TYPE) + + resultant_data = np.frombuffer(result_tensor.data, np.float32) + resultant_data = np.reshape(resultant_data, tuple(RESULTANT_DIMS)) + for i in range(DIMENSION_CHANNELS): + self.assertTrue((resultant_data[0, :, :, i] == i).all()) + + finally: + self.node.destroy_subscription(subs) + self.node.destroy_publisher(tensor_pub) diff --git a/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/camera_info.json b/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/camera_info.json new file mode 100644 index 0000000..396a04b --- /dev/null +++ b/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/camera_info.json @@ -0,0 +1,51 @@ +{ + "header": { + "frame_id": "tf_camera" + }, + "width": 852, + "height": 480, + "distortion_model": "plumb_bob", + "D": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "K": [ + 651.2994384765625, + 0.000000, + 298.3225504557292, + 0.0, + 651.2994384765625, + 392.1635182698568, + 0.0, + 0.0, + 1.0 + ], + "R": [ + 1.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 1.0 + ], + "P": [ + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0 + ] +} diff --git a/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.jpg b/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.jpg new file mode 100644 index 0000000..14e05ef --- /dev/null +++ b/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a8d03e3f42eea75c733e710d3444fbeb95b0fd69c1035357a64d657d4a5e6fb5 +size 336079 diff --git a/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.json b/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.json new file mode 100644 index 0000000..53d0f37 --- /dev/null +++ b/isaac_ros_tensor_proc/test/test_cases/pose_estimation_0/image.json @@ -0,0 +1,4 @@ +{ + "image": "image.jpg", + "encoding": "bgr8" +} \ No newline at end of file diff --git a/isaac_ros_tensor_rt/CMakeLists.txt b/isaac_ros_tensor_rt/CMakeLists.txt index 79716dc..de8b846 100644 --- a/isaac_ros_tensor_rt/CMakeLists.txt +++ b/isaac_ros_tensor_rt/CMakeLists.txt @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -25,24 +25,31 @@ endif() find_package(ament_cmake_auto REQUIRED) ament_auto_find_build_dependencies() +# Dependencies +find_package(TENSORRT 8 MODULE REQUIRED) + # TensorRTNode ament_auto_add_library(tensor_rt_node SHARED src/tensor_rt_node.cpp) target_link_libraries(tensor_rt_node TENSORRT::nvonnxparser) rclcpp_components_register_nodes(tensor_rt_node "nvidia::isaac_ros::dnn_inference::TensorRTNode") set(node_plugins "${node_plugins}nvidia::isaac_ros::dnn_inference::TensorRTNode;$\n") - -### Install extensions built from source - -# TensorRT -add_subdirectory(gxf/tensor_rt) -install(TARGETS gxf_tensor_rt DESTINATION share/${PROJECT_NAME}/gxf/tensor_rt) - -### End extensions +set_target_properties(tensor_rt_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE +) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() + # Add gtests + ament_add_gtest(tensor_rt_node_test test/tensor_rt_node_test.cpp) + target_link_libraries(tensor_rt_node_test tensor_rt_node) + target_include_directories(tensor_rt_node_test PUBLIC include/isaac_ros_tensor_rt/) + target_include_directories(tensor_rt_node_test PUBLIC /usr/src/googletest/googlemock/include/) + ament_target_dependencies(tensor_rt_node_test rclcpp) + ament_target_dependencies(tensor_rt_node_test isaac_ros_nitros) # The FindPythonInterp and FindPythonLibs modules are removed if(POLICY CMP0148) diff --git a/isaac_ros_tensor_rt/config/isaac_ros_tensor_rt.yaml b/isaac_ros_tensor_rt/config/isaac_ros_tensor_rt.yaml new file mode 100644 index 0000000..e6f58f3 --- /dev/null +++ b/isaac_ros_tensor_rt/config/isaac_ros_tensor_rt.yaml @@ -0,0 +1,58 @@ +%YAML 1.2 +# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +# +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. +--- +id: [0xd43f23e4b9bf11eb, 0x9d182b7be630552b] +name: TensorRTExtension +version: 2.0.0 +components: + - id: [0x06a7f0e0b9c011eb, 0x8cd623c9c2070107] + type: nvidia::gxf::TensorRtInference + input_output_groups: + - input_keys: [rx] + output_keys: [tx] + input_format_keys: [] + output_format_keys: [] + supported_formats: + - platforms: [any] + details: + - input_formats: [nitros_tensor_list_nchw] + output_formats: [nitros_tensor_list_nchw] + costs: + throughput: 10bytes/s + latency: 10ms + power: 100J + accuracy: 100% + - input_formats: [nitros_tensor_list_nhwc] + output_formats: [nitros_tensor_list_nhwc] + costs: + throughput: 10bytes/s + latency: 10ms + power: 100J + accuracy: 100% + - input_formats: [nitros_tensor_list_nchw_rgb_f32] + output_formats: [nitros_tensor_list_nhwc_rgb_f32] + costs: + throughput: 10bytes/s + latency: 10ms + power: 100J + accuracy: 100% + - input_formats: [nitros_tensor_list_nchw_bgr_f32] + output_formats: [nitros_tensor_list_nhwc_bgr_f32] + costs: + throughput: 10bytes/s + latency: 10ms + power: 100J + accuracy: 100% + - input_formats: [nitros_tensor_list_nchw_rgb_f32] + output_formats: [nitros_tensor_list_nchw_rgb_f32] + costs: + throughput: 10bytes/s + latency: 10ms + power: 100J + accuracy: 100% \ No newline at end of file diff --git a/isaac_ros_tensor_rt/config/tensor_rt_inference.yaml b/isaac_ros_tensor_rt/config/tensor_rt_inference.yaml index cb3b379..5e9e8cc 100644 --- a/isaac_ros_tensor_rt/config/tensor_rt_inference.yaml +++ b/isaac_ros_tensor_rt/config/tensor_rt_inference.yaml @@ -22,7 +22,6 @@ components: type: nvidia::gxf::DoubleBufferTransmitter parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::DownstreamReceptiveSchedulingTerm parameters: transmitter: tx @@ -35,7 +34,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - name: pool type: nvidia::gxf::BlockMemoryPool parameters: @@ -56,7 +54,6 @@ components: - mobilenetv20_output_flatten0_reshape0 pool: pool cuda_stream_pool: utils/stream - dummy_rx: rx rx: [rx] tx: tx force_engine_update: true @@ -73,12 +70,10 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 2 - policy: 0 - name: tx type: nvidia::gxf::DoubleBufferTransmitter parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: rx @@ -98,7 +93,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 1 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: signal diff --git a/isaac_ros_tensor_rt/gxf/AMENT_IGNORE b/isaac_ros_tensor_rt/gxf/AMENT_IGNORE deleted file mode 100644 index e69de29..0000000 diff --git a/isaac_ros_tensor_rt/include/isaac_ros_tensor_rt/tensor_rt_node.hpp b/isaac_ros_tensor_rt/include/isaac_ros_tensor_rt/tensor_rt_node.hpp index aaf2ec7..fc6977a 100644 --- a/isaac_ros_tensor_rt/include/isaac_ros_tensor_rt/tensor_rt_node.hpp +++ b/isaac_ros_tensor_rt/include/isaac_ros_tensor_rt/tensor_rt_node.hpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_tensor_rt/launch/isaac_ros_tensor_rt.launch.py b/isaac_ros_tensor_rt/launch/isaac_ros_tensor_rt.launch.py index dc29473..dc8aa51 100644 --- a/isaac_ros_tensor_rt/launch/isaac_ros_tensor_rt.launch.py +++ b/isaac_ros_tensor_rt/launch/isaac_ros_tensor_rt.launch.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/isaac_ros_tensor_rt/package.xml b/isaac_ros_tensor_rt/package.xml index 492d210..4d2d91c 100644 --- a/isaac_ros_tensor_rt/package.xml +++ b/isaac_ros_tensor_rt/package.xml @@ -21,10 +21,10 @@ SPDX-License-Identifier: Apache-2.0 isaac_ros_tensor_rt - 2.1.0 + 3.0.0 DNN Inference support for Isaac ROS - CY Chen + Isaac ROS Maintainers Apache-2.0 https://developer.nvidia.com/isaac-ros-gems/ CY Chen @@ -42,9 +42,12 @@ SPDX-License-Identifier: Apache-2.0 isaac_ros_common + gxf_isaac_tensor_rt + ament_lint_auto ament_lint_common isaac_ros_test + ament_cmake_gtest ament_cmake diff --git a/isaac_ros_tensor_rt/src/tensor_rt_node.cpp b/isaac_ros_tensor_rt/src/tensor_rt_node.cpp index 22843a0..f6f6c54 100644 --- a/isaac_ros_tensor_rt/src/tensor_rt_node.cpp +++ b/isaac_ros_tensor_rt/src/tensor_rt_node.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -55,12 +55,14 @@ const std::vector> EXTENSIONS = { {"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"}, {"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"}, {"isaac_ros_gxf", "gxf/lib/serialization/libgxf_serialization.so"}, - {"isaac_ros_tensor_rt", "gxf/tensor_rt/libgxf_tensor_rt.so"} + {"gxf_isaac_tensor_rt", "gxf/lib/libgxf_isaac_tensor_rt.so"} }; const std::vector PRESET_EXTENSION_SPEC_NAMES = { "isaac_ros_tensor_rt", }; -const std::vector EXTENSION_SPEC_FILENAMES = {}; +const std::vector EXTENSION_SPEC_FILENAMES = { + "config/isaac_ros_tensor_rt.yaml" +}; const std::vector GENERATOR_RULE_FILENAMES = { "config/namespace_injector_rule.yaml" }; diff --git a/isaac_ros_tensor_rt/test/isaac_ros_tensor_rt_test.py b/isaac_ros_tensor_rt/test/isaac_ros_tensor_rt_test.py index b64081a..f98b502 100644 --- a/isaac_ros_tensor_rt/test/isaac_ros_tensor_rt_test.py +++ b/isaac_ros_tensor_rt/test/isaac_ros_tensor_rt_test.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/isaac_ros_tensor_rt/test/tensor_rt_node_test.cpp b/isaac_ros_tensor_rt/test/tensor_rt_node_test.cpp new file mode 100644 index 0000000..b551c1c --- /dev/null +++ b/isaac_ros_tensor_rt/test/tensor_rt_node_test.cpp @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES +// Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include "tensor_rt_node.hpp" +#include "rclcpp/rclcpp.hpp" + + +TEST(tensor_rt_node_test, test_engine_file_path) +{ + rclcpp::init(0, nullptr); + rclcpp::NodeOptions options; + options.arguments( + { + "--ros-args", + "-p", "engine_file_path:=''", + }); + EXPECT_THROW( + { + try { + nvidia::isaac_ros::dnn_inference::TensorRTNode trt_node(options); + } catch (const std::invalid_argument & e) { + EXPECT_THAT(e.what(), testing::HasSubstr("Empty engine_file_path")); + throw; + } + }, std::invalid_argument); + rclcpp::shutdown(); +} + + +int main(int argc, char ** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/isaac_ros_triton/CMakeLists.txt b/isaac_ros_triton/CMakeLists.txt index b9369ef..3e4e26e 100644 --- a/isaac_ros_triton/CMakeLists.txt +++ b/isaac_ros_triton/CMakeLists.txt @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,9 +22,6 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() -execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) -message( STATUS "Architecture: ${ARCHITECTURE}" ) - find_package(ament_cmake_auto REQUIRED) ament_auto_find_build_dependencies() @@ -32,19 +29,11 @@ ament_auto_find_build_dependencies() ament_auto_add_library(triton_node SHARED src/triton_node.cpp) rclcpp_components_register_nodes(triton_node "nvidia::isaac_ros::dnn_inference::TritonNode") set(node_plugins "${node_plugins}nvidia::isaac_ros::dnn_inference::TritonNode;$\n") - -### Install extensions built from source - -# Triton + dependencies -add_subdirectory(gxf/triton) -install(TARGETS gxf_triton_ext DESTINATION share/${PROJECT_NAME}/gxf/triton/) -if( ${ARCHITECTURE} STREQUAL "x86_64" ) - set(ARCH_GXF_PATH "gxf_x86_64_cuda_11_8") - elseif( ${ARCHITECTURE} STREQUAL "aarch64" ) - set(ARCH_GXF_PATH "gxf_jetpack502") -endif() -install(DIRECTORY gxf/triton/nvds/lib/${ARCH_GXF_PATH}/ - DESTINATION share/${PROJECT_NAME}/gxf/triton) +set_target_properties(triton_node PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + BUILD_RPATH_USE_ORIGIN TRUE + INSTALL_RPATH_USE_LINK_PATH TRUE +) if(BUILD_TESTING) find_package(ament_lint_auto REQUIRED) diff --git a/isaac_ros_triton/config/triton_node.yaml b/isaac_ros_triton/config/triton_node.yaml index f508cc3..fa09553 100644 --- a/isaac_ros_triton/config/triton_node.yaml +++ b/isaac_ros_triton/config/triton_node.yaml @@ -31,7 +31,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: input @@ -67,7 +66,6 @@ components: type: nvidia::gxf::DoubleBufferTransmitter parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::DownstreamReceptiveSchedulingTerm parameters: transmitter: output @@ -87,7 +85,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: input @@ -96,7 +93,6 @@ components: type: nvidia::gxf::DoubleBufferTransmitter parameters: capacity: 12 - policy: 0 - type: nvidia::gxf::DownstreamReceptiveSchedulingTerm parameters: transmitter: output @@ -116,7 +112,6 @@ components: type: nvidia::gxf::DoubleBufferReceiver parameters: capacity: 1 - policy: 0 - type: nvidia::gxf::MessageAvailableSchedulingTerm parameters: receiver: signal @@ -141,6 +136,6 @@ components: parameters: clock: clock stop_on_deadlock: false - check_recession_period_us: 100 + check_recession_period_ms: 0.1 - name: clock type: nvidia::gxf::RealtimeClock diff --git a/isaac_ros_triton/gxf/AMENT_IGNORE b/isaac_ros_triton/gxf/AMENT_IGNORE deleted file mode 100644 index e69de29..0000000 diff --git a/isaac_ros_triton/gxf/triton/CMakeLists.txt b/isaac_ros_triton/gxf/triton/CMakeLists.txt deleted file mode 100644 index f5dfa94..0000000 --- a/isaac_ros_triton/gxf/triton/CMakeLists.txt +++ /dev/null @@ -1,115 +0,0 @@ -# SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 - -project(gxf_triton_ext LANGUAGES C CXX) - -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-fPIC -w) -endif() - -set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) -set(LAST_CMAKE_MESSAGE_LOG_LEVEL ${CMAKE_MESSAGE_LOG_LEVEL}) -set(CMAKE_MESSAGE_LOG_LEVEL ERROR) - -# Dependencies -include(FetchContent) -find_package(yaml-cpp) -find_package(GXF ${ISAAC_ROS_GXF_VERSION} MODULE REQUIRED - COMPONENTS - core - std -) -# Lock version of Protocol buffers for compatibility with pre-built NVDS -set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) -set(protobuf_BUILD_TESTS OFF) -set(protobuf_BUILD_EXPORT OFF) -set(protobuf_MSVC_STATIC_RUNTIME OFF) -set(Protobuf_USE_STATIC_LIBS ON) -set(Protobuf_BUILD_SHARED_LIBS OFF) - -fetchcontent_declare( - protobuf - GIT_REPOSITORY https://github.com/protocolbuffers/protobuf.git - GIT_TAG v3.8.0 - SOURCE_SUBDIR cmake -) -fetchcontent_makeavailable(protobuf) -find_package(Protobuf QUIET) -set(CMAKE_MESSAGE_LOG_LEVEL ${LAST_CMAKE_MESSAGE_LOG_LEVEL}) - -# Compile protocol buffers -file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/nvds/include/*.proto") -PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles}) -add_library(libgxf_triton_proto STATIC ${ProtoSources} ${ProtoHeaders}) -target_link_libraries(libgxf_triton_proto PUBLIC protobuf::libprotobuf) - -# NVDS pre-built -add_library(libs_triton::libnvbuf_fdmap SHARED IMPORTED) -add_library(libs_triton::libnvbufsurface SHARED IMPORTED) -add_library(libs_triton::libnvbufsurftransform SHARED IMPORTED) -add_library(libs_triton::libnvds_infer_server SHARED IMPORTED) -add_library(libs_triton::libnvds_inferlogger SHARED IMPORTED) -add_library(libs_triton::libnvds_inferutils SHARED IMPORTED) -add_library(libs_triton::libs_triton INTERFACE IMPORTED) -set_property(TARGET libs_triton::libs_triton PROPERTY - INTERFACE_LINK_LIBRARIES - libs_triton::libnvbuf_fdmap - libs_triton::libnvbufsurface - libs_triton::libnvbufsurftransform - libs_triton::libnvds_infer_server - libs_triton::libnvds_inferlogger - libs_triton::libnvds_inferutils -) - -execute_process(COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE) -message( STATUS "Architecture: ${ARCHITECTURE}" ) -if( ${ARCHITECTURE} STREQUAL "x86_64" ) - set(ARCH_GXF_PATH "gxf_x86_64_cuda_11_8") - elseif( ${ARCHITECTURE} STREQUAL "aarch64" ) - set(ARCH_GXF_PATH "gxf_jetpack502") -endif() -set_property(TARGET libs_triton::libnvbuf_fdmap PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/${ARCH_GXF_PATH}/libnvbuf_fdmap.so) -set_property(TARGET libs_triton::libnvbufsurface PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/${ARCH_GXF_PATH}/libnvbufsurface.so) -set_property(TARGET libs_triton::libnvbufsurftransform PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/${ARCH_GXF_PATH}/libnvbufsurftransform.so) -set_property(TARGET libs_triton::libnvds_infer_server PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/${ARCH_GXF_PATH}/libnvds_infer_server.so) -set_property(TARGET libs_triton::libnvds_inferlogger PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/${ARCH_GXF_PATH}/libnvds_inferlogger.so) -set_property(TARGET libs_triton::libnvds_inferutils PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/nvds/lib/${ARCH_GXF_PATH}/libnvds_inferutils.so) - -# Triton extension -add_library(gxf_triton_ext SHARED - extensions/triton/triton_server.cpp - inferencers/triton_inferencer_impl.cpp - triton_ext.cpp - triton_inference_request.cpp - triton_inference_response.cpp - triton_scheduling_terms.cpp -) -set(CMAKE_INCLUDE_CURRENT_DIR TRUE) -target_include_directories(gxf_triton_ext PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/nvds/include - ${CMAKE_CURRENT_SOURCE_DIR}/extensions/triton -) -target_link_libraries(gxf_triton_ext - PUBLIC - GXF::std - libgxf_triton_proto - libs_triton::libs_triton - yaml-cpp -) -set_target_properties(gxf_triton_ext PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE) -set_target_properties(gxf_triton_ext PROPERTIES INSTALL_RPATH "$ORIGIN") - diff --git a/isaac_ros_triton/gxf/triton/extensions/triton/triton_options.hpp b/isaac_ros_triton/gxf/triton/extensions/triton/triton_options.hpp deleted file mode 100644 index b310695..0000000 --- a/isaac_ros_triton/gxf/triton/extensions/triton/triton_options.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_TRITON_OPTIONS_HPP -#define NVIDIA_TRITON_TRITON_OPTIONS_HPP - -namespace nvidia { -namespace triton { - -/** - * @brief Triton Inference Options for model control and sequence control - * - */ -struct TritonOptions { - uint64_t sequence_id; // Should be non-zero because zero is reserved for non-sequence requests. - bool start; - bool end; - uint64_t priority; - uint64_t timeout; -}; - -} // namespace triton -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/gxf/triton/extensions/triton/triton_server.cpp b/isaac_ros_triton/gxf/triton/extensions/triton/triton_server.cpp deleted file mode 100644 index 2a794cd..0000000 --- a/isaac_ros_triton/gxf/triton/extensions/triton/triton_server.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include -#include -#include - -#include "common/logger.hpp" -#include "nvdsinferserver_config.pb.h" - -#include "triton_server.hpp" - -namespace nvidia { -namespace triton { - -gxf_result_t TritonServer::initialize() { - GXF_LOG_DEBUG("Initializing Triton Server..."); - - nvdsinferserver::config::TritonModelRepo model_repo_config; - - model_repo_config.set_log_level(log_level_.get()); - model_repo_config.set_strict_model_config(enable_strict_model_config_.get()); - model_repo_config.set_min_compute_capacity(static_cast(min_compute_capability_.get())); - for (const auto& model_repository_path : model_repository_paths_.get()) { - model_repo_config.add_root(model_repository_path); - } - model_repo_config.set_tf_gpu_memory_fraction(static_cast(tf_gpu_memory_fraction_.get())); - model_repo_config.set_tf_disable_soft_placement(tf_disable_soft_placement_.get()); - model_repo_config.set_backend_dir(backend_directory_path_.get()); - model_repo_config.set_model_control_mode(model_control_mode_.get()); - - size_t num_backend_config = 0; - const char delim_setting = ','; - const char delim_value = '='; - - auto maybe_backend_configs = backend_configs_.try_get(); - if (maybe_backend_configs) { - for (const auto& config : maybe_backend_configs.value()) { - model_repo_config.add_backend_configs(); - auto proto_config = model_repo_config.mutable_backend_configs(num_backend_config++); - - size_t delim_setting_pos = config.find(delim_setting); - if (delim_setting_pos == std::string::npos) { - GXF_LOG_ERROR("Unable to find '%c' in backend config: %s", delim_setting, config.c_str()); - return GXF_FAILURE; - } - size_t delim_value_pos = config.find(delim_value, delim_setting_pos); - if (delim_value_pos == std::string::npos) { - GXF_LOG_ERROR("Unable to find '%c' in backend config: %s", delim_value, config.c_str()); - return GXF_FAILURE; - } - if (delim_setting_pos >= delim_value_pos) { - GXF_LOG_ERROR("Delimeter '%c' must come before '%c' in backend config: %s", - delim_setting, delim_value, config.c_str()); - return GXF_FAILURE; - } - - const std::string backend_name = config.substr(0, delim_setting_pos); - const std::string backend_setting = config.substr(delim_setting_pos + 1, - delim_value_pos - delim_setting_pos - 1); - const std::string backend_value = config.substr(delim_value_pos + 1); - - proto_config->set_backend(backend_name); - proto_config->set_setting(backend_setting); - proto_config->set_value(backend_value); - } - } - - tritonRepoConfig_ = std::make_shared(model_repo_config); - - nvdsinferserver::ITritonServerInstance* server_ptr = nullptr; - auto result = NvDsTritonServerInit(&server_ptr, model_repo_config.DebugString().c_str(), - model_repo_config.DebugString().size()); - if (result != NvDsInferStatus::NVDSINFER_SUCCESS) { - GXF_LOG_ERROR("Error in NvDsTritonServerInit"); - return GXF_FAILURE; - } - - std::shared_ptr server( - server_ptr, NvDsTritonServerDeinit); - tritonInstance_ = std::move(server); - GXF_LOG_DEBUG("Successfully initialized Triton Server..."); - return GXF_SUCCESS; -} - -nvidia::gxf::Expected> -TritonServer::getServer() { - if (!tritonInstance_) { - return nvidia::gxf::Unexpected{GXF_NULL_POINTER}; - } - return tritonInstance_; -} - -nvidia::gxf::Expected> -TritonServer::getModelRepoConfig() { - if (!tritonRepoConfig_) { - return nvidia::gxf::Unexpected{GXF_NULL_POINTER}; - } - return tritonRepoConfig_; -} - - -} // namespace triton -} // namespace nvidia diff --git a/isaac_ros_triton/gxf/triton/extensions/triton/triton_server.hpp b/isaac_ros_triton/gxf/triton/extensions/triton/triton_server.hpp deleted file mode 100644 index d926719..0000000 --- a/isaac_ros_triton/gxf/triton/extensions/triton/triton_server.hpp +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_TRITON_SERVER_HPP -#define NVIDIA_TRITON_TRITON_SERVER_HPP - -#include -#include -#include - -#include "gxf/core/component.hpp" -#include "gxf/core/entity.hpp" -#include "gxf/core/expected.hpp" -#include "gxf/core/handle.hpp" - -#include "infer_icontext.h" -#include "nvdsinferserver_config.pb.h" - -namespace nvidia { -namespace triton { - -class TritonServer : public nvidia::gxf::Component { - public: - gxf_result_t registerInterface(nvidia::gxf::Registrar* registrar) override { - nvidia::gxf::Expected result; - - result &= registrar->parameter(log_level_, "log_level", - "Triton Logging Level", - "Set verbose logging level. 0 = Error, 1 = Warn, 2 = Info, 3+ = Verbose", 1U); - - result &= registrar->parameter(enable_strict_model_config_, - "enable_strict_model_config", - "Enable Strict Model Configuration", - "Enable Strict Model Configuration to enforce presence of config. " - "If disabled, TensorRT, TensorFlow saved-model, and ONNX models do " - "not require a model configuration file because Triton can derive " - "all the required settings automatically", true); - - result &= registrar->parameter(min_compute_capability_, - "min_compute_capability", - "Minimum Compute Capability", - "Minimum Compute Capability for GPU. " - "Refer to https://developer.nvidia.com/cuda-gpus", 6.0); - - result &= registrar->parameter(model_repository_paths_, - "model_repository_paths", - "List of Triton Model Repository Paths", - "List of Triton Model Repository Paths. Refer to " - "https://github.com/bytedance/triton-inference-server/blob/master/docs/" - "model_repository.md"); - - result &= registrar->parameter(tf_gpu_memory_fraction_, - "tf_gpu_memory_fraction", - "Tensorflow GPU Memory Fraction", - "The portion of GPU memory to be reserved for TensorFlow Models.", 0.0); - - result &= registrar->parameter(tf_disable_soft_placement_, - "tf_disable_soft_placement", - "Tensorflow will use CPU operation when GPU implementation is not available", - "Tensorflow will use CPU operation when GPU implementation is not available", true); - - result &= registrar->parameter(backend_directory_path_, - "backend_directory_path", - "Path to Triton Backend Directory", - "Path to Triton Backend Directory", std::string("")); - - // TODO(@niralp): Design GXF Model Explicit mode - result &= registrar->parameter(model_control_mode_, - "model_control_mode", - "Triton Model Control Mode", - "Triton Model Control Mode. 'none' will load all models at startup. 'explicit' " - "will allow load of models when needed. Unloading is unsupported", std::string("explicit")); - - result &= registrar->parameter(backend_configs_, - "backend_configs", - "Triton Backend Configurations", - "Triton Backend Configurations in format: 'backend,setting=value'. " - "Refer to Backend specific documentation: " - "https://github.com/triton-inference-server/tensorflow_backend#command-line-options, " - "https://github.com/triton-inference-server/python_backend#managing-shared-memory", - nvidia::gxf::Registrar::NoDefaultParameter(), GXF_PARAMETER_FLAGS_OPTIONAL); - - return nvidia::gxf::ToResultCode(result); - } - - /** - * @brief Create Triton Server via nvdsinferserver::ITritonServerInstance with parameters. - * - * @details Create Shared instance with destructor. - * - * @return gxf_result_t - */ - gxf_result_t initialize() override; - - - /** - * @brief Get the shared instance of nvdsinferserver::ITritonServerInstance. - * - * @details Shared ownership is necessary for proper deinitialization of the underlying Triton - * server since GXF lacks guarantees on deinitialize() ordering across multiple entities. - * - * @return nvidia::gxf::Expected> - */ - nvidia::gxf::Expected> getServer(); - - /** - * @brief Get the shared instance of config::TritonModelRepo. - * - * @details Shared ownership is necessary for proper deinitialization of the underlying Triton - * server since GXF lacks guarantees on deinitialize() ordering across multiple entities. - * - * @return nvidia::gxf::Expected> - */ - nvidia::gxf::Expected> - getModelRepoConfig(); - - private: - // Parameters supported by nvdsinferserver::config::TritonModelRepo - nvidia::gxf::Parameter log_level_; - nvidia::gxf::Parameter enable_strict_model_config_; - nvidia::gxf::Parameter min_compute_capability_; - nvidia::gxf::Parameter> model_repository_paths_; - - nvidia::gxf::Parameter tf_gpu_memory_fraction_; - nvidia::gxf::Parameter tf_disable_soft_placement_; - - nvidia::gxf::Parameter backend_directory_path_; - nvidia::gxf::Parameter model_control_mode_; - - nvidia::gxf::Parameter> backend_configs_; - - - // Shared instance is needed for proper deinitialize since this will be needed for each inference - // request. - std::shared_ptr tritonInstance_; - - // Shared instance is needed for constructing the inference config - std::shared_ptr tritonRepoConfig_; -}; - - -} // namespace triton -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.cpp b/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.cpp deleted file mode 100644 index ea13766..0000000 --- a/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.cpp +++ /dev/null @@ -1,781 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "triton_inferencer_impl.hpp" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "gxf/std/tensor.hpp" -#include "gxf/std/timestamp.hpp" - -#include "extensions/triton/triton_options.hpp" - -#include "infer_icontext.h" -#include "infer_options.h" -#include "nvdsinferserver_config.pb.h" - -namespace nvidia { -namespace triton { - -struct Inference { - // Entity that will preserve lifetime for input tensors for the Inference Request - std::vector preserved_input; - - // Raw NvDs Ouptuts of the Inference Request - // WAR: The ownership should ideally be handed to a GXF Tensor: GXF-204 - nvdsinferserver::SharedIBatchArray raw_output; - - // Inference Status to be modified by Inference Request completion - NvDsInferStatus infer_status { NVDSINFER_SUCCESS }; - - // Future event indicates completion via callback - std::promise response_promise; - std::future response_completion; - - // Indicates whether this inference is currently active; this can be asynchronously accessed - // via isAcceptingRequest() - std::atomic is_active = { false }; - - // Indicates whether this inference is complete - bool is_complete = { false }; -}; - -/** - * @brief Helper function for translating TRITONSERVER_DataType to nvidia::gxf::PrimitiveType - * - * - * @param datatype - * @return nvidia::gxf::PrimitiveType - */ -static nvidia::gxf::PrimitiveType NvDsToGxfDataType(nvdsinferserver::InferDataType datatype) { - // Unsupported: - // nvdsinferserver::InferDataType::kFp16 - // nvdsinferserver::InferDataType::kString - // nvdsinferserver::InferDataType::kBool - static const std::unordered_map - sNvDsToGxf{ - {nvdsinferserver::InferDataType::kUint8, nvidia::gxf::PrimitiveType::kUnsigned8}, - {nvdsinferserver::InferDataType::kUint16, nvidia::gxf::PrimitiveType::kUnsigned16}, - {nvdsinferserver::InferDataType::kUint32, nvidia::gxf::PrimitiveType::kUnsigned32}, - {nvdsinferserver::InferDataType::kUint64, nvidia::gxf::PrimitiveType::kUnsigned64}, - {nvdsinferserver::InferDataType::kInt8, nvidia::gxf::PrimitiveType::kInt8}, - {nvdsinferserver::InferDataType::kInt16, nvidia::gxf::PrimitiveType::kInt16}, - {nvdsinferserver::InferDataType::kInt32, nvidia::gxf::PrimitiveType::kInt32}, - {nvdsinferserver::InferDataType::kInt64, nvidia::gxf::PrimitiveType::kInt64}, - {nvdsinferserver::InferDataType::kFp32, nvidia::gxf::PrimitiveType::kFloat32}, - {nvdsinferserver::InferDataType::kFp64, nvidia::gxf::PrimitiveType::kFloat64}, - {nvdsinferserver::InferDataType::kString, nvidia::gxf::PrimitiveType::kCustom}, - }; - auto const i = sNvDsToGxf.find(datatype); - if (i == sNvDsToGxf.end()) { - GXF_LOG_WARNING("Unsupported NvDs data type: %d", datatype); - return nvidia::gxf::PrimitiveType::kCustom; - } - return i->second; -} - -/** - * @brief Helper function for translating nvidia::gxf::PrimitiveType to - * nvdsinferserver::InferDataType - * - * @param datatype - * @return nvdsinferserver::InferDataType - */ -static nvdsinferserver::InferDataType GxfToNvDsDataType( - nvidia::gxf::PrimitiveType datatype) { - static const std::unordered_map - sGxfToNvDsData{ - {nvidia::gxf::PrimitiveType::kUnsigned8, nvdsinferserver::InferDataType::kUint8}, - {nvidia::gxf::PrimitiveType::kUnsigned16, nvdsinferserver::InferDataType::kUint16}, - {nvidia::gxf::PrimitiveType::kUnsigned32, nvdsinferserver::InferDataType::kUint32}, - {nvidia::gxf::PrimitiveType::kUnsigned64, nvdsinferserver::InferDataType::kUint64}, - {nvidia::gxf::PrimitiveType::kInt8, nvdsinferserver::InferDataType::kUint8}, - {nvidia::gxf::PrimitiveType::kInt16, nvdsinferserver::InferDataType::kInt16}, - {nvidia::gxf::PrimitiveType::kInt32, nvdsinferserver::InferDataType::kInt32}, - {nvidia::gxf::PrimitiveType::kInt64, nvdsinferserver::InferDataType::kInt64}, - {nvidia::gxf::PrimitiveType::kFloat32, nvdsinferserver::InferDataType::kFp32}, - {nvidia::gxf::PrimitiveType::kFloat64, nvdsinferserver::InferDataType::kFp64}, - {nvidia::gxf::PrimitiveType::kCustom, nvdsinferserver::InferDataType::kString}, - }; - // NOTE: Unsupported nvdsinferserver::InferDataType are: - // - kFp16 - // - kBool - // - kNone - auto const i = sGxfToNvDsData.find(datatype); - if (i == sGxfToNvDsData.end()) { - GXF_LOG_WARNING("Unsupported GXF data type: %d", datatype); - return nvdsinferserver::InferDataType::kNone; - } - return i->second; -} - -/** - * @brief Helper function for translating nvidia::gxf::MemoryStorageType to - * nvdsinferserver::InferMemType - * - * @param memory_type - * @return nvdsinferserver::InferMemType - */ -static nvdsinferserver::InferMemType GxfMemTypeToNvDsMemType( - nvidia::gxf::MemoryStorageType memory_type) { - static const std::unordered_map - sGxfToNvDsMem{ - {nvidia::gxf::MemoryStorageType::kHost, nvdsinferserver::InferMemType::kCpu}, - {nvidia::gxf::MemoryStorageType::kSystem, nvdsinferserver::InferMemType::kCpuCuda}, - {nvidia::gxf::MemoryStorageType::kDevice, nvdsinferserver::InferMemType::kGpuCuda}, - }; - auto const i = sGxfToNvDsMem.find(memory_type); - if (i == sGxfToNvDsMem.end()) { - GXF_LOG_WARNING("Unsupported GXF data type: %d", memory_type); - return nvdsinferserver::InferMemType::kNone; - } - return i->second; -} - -static nvdsinferserver::config::MemoryType GxfMemTypeToNvDsConfMemType( - nvidia::gxf::MemoryStorageType memory_type) { - static const std::unordered_map - sGxfToNvDsMem{ - {nvidia::gxf::MemoryStorageType::kHost, - nvdsinferserver::config::MemoryType::MEMORY_TYPE_CPU}, - {nvidia::gxf::MemoryStorageType::kSystem, - nvdsinferserver::config::MemoryType::MEMORY_TYPE_CPU}, - {nvidia::gxf::MemoryStorageType::kDevice, - nvdsinferserver::config::MemoryType::MEMORY_TYPE_GPU}, - }; - auto const i = sGxfToNvDsMem.find(memory_type); - if (i == sGxfToNvDsMem.end()) { - GXF_LOG_WARNING("Unsupported GXF data type: %d", memory_type); - return nvdsinferserver::config::MemoryType::MEMORY_TYPE_DEFAULT; - } - return i->second; -} - -static nvidia::gxf::MemoryStorageType NvDsMemTypeToGxfMemType( - nvdsinferserver::InferMemType memory_type) { - static const std::unordered_map - sNvDsMemToGxf{ - {nvdsinferserver::InferMemType::kCpu, nvidia::gxf::MemoryStorageType::kHost}, - {nvdsinferserver::InferMemType::kCpuCuda, nvidia::gxf::MemoryStorageType::kSystem}, - {nvdsinferserver::InferMemType::kGpuCuda, nvidia::gxf::MemoryStorageType::kDevice}, - }; - auto const i = sNvDsMemToGxf.find(memory_type); - GXF_ASSERT(i != sNvDsMemToGxf.end(), "Unsupported conversion from NvDs data type: %d", - memory_type); - return i->second; -} - -static gxf_result_t setTritonOptions( - nvdsinferserver::SharedIBatchArray& batchArray, const TritonOptions& triton_options) { - nvdsinferserver::SharedBufOptions options = std::make_shared(); - if (!options) { - GXF_LOG_ERROR("Unable to create Triton Options: SharedBufOptions"); - return GXF_NULL_POINTER; - } - options->setValue(OPTION_SEQUENCE_ID, triton_options.sequence_id); - options->setValue(OPTION_SEQUENCE_START, triton_options.start); - options->setValue(OPTION_SEQUENCE_END, triton_options.end); - options->setValue(OPTION_PRIORITY, triton_options.priority); - options->setValue(OPTION_TIMEOUT, triton_options.timeout); - - if (!batchArray) { - GXF_LOG_ERROR("Input batch array for setting Triton Options is null"); - return GXF_NULL_POINTER; - } - batchArray->setIOptions(std::move(options)); - if (!batchArray->getOptions()) { - GXF_LOG_ERROR("Batch array unable to getOptions()"); - return GXF_NULL_POINTER; - } - return GXF_SUCCESS; -} - -gxf_result_t TritonInferencerImpl::initialize() { - scheduling_term_->setEventState(nvidia::gxf::AsynchronousEventState::WAIT); - - // Initialize pool of Inference Requests. This is needed to occur before start() for proper - // behavior with Scheduling Terms dependent upon isAcceptingRequest(). - inference_pool_.resize(num_concurrent_requests_.get()); - for (size_t num = 0; num < num_concurrent_requests_.get(); num++) { - inference_pool_[num] = new Inference(); - } - if (!inference_pool_.size()) { - GXF_LOG_ERROR("Inference Pool Empty"); - return GXF_FAILURE; - } - return GXF_SUCCESS; -} - -gxf_result_t TritonInferencerImpl::construct() { - if (!inference_pool_.size()) { - GXF_LOG_ERROR("Inference Pool Empty"); - return GXF_FAILURE; - } - - if (nvidia::gxf::Shape::kMaxRank != NVDSINFER_MAX_DIMS) { - GXF_LOG_WARNING("GXF and NvDs Max Rank are mistmatched, which may cause problems."); - } - - nvdsinferserver::config::InferenceConfig inference_config; - inference_config.set_unique_id(static_cast(eid())); - inference_config.set_max_batch_size(max_batch_size_.get()); - inference_config.mutable_backend()->mutable_triton()->set_model_name(model_name_.get()); - inference_config.mutable_backend()->mutable_triton()->set_version(model_version_.get()); - inference_config.add_gpu_ids(0); - - // ensure no pre or post processing is attached - inference_config.clear_preprocess(); - inference_config.clear_postprocess(); - - if (inference_mode_.get() == TritonInferenceMode::kDirect) { - if (!server_handle_.try_get()) { - GXF_LOG_ERROR("Triton Server Handle is null with Direct inference mode"); - return GXF_ARGUMENT_INVALID; - } - auto maybe_server = server_handle_.try_get().value()->getServer(); - if (!maybe_server) { - GXF_LOG_ERROR("Triton Server is unexpected"); - return nvidia::gxf::ToResultCode(maybe_server); - } - server_ = maybe_server.value(); - - auto maybe_model_repo = server_handle_.try_get().value()->getServer(); - if (!maybe_model_repo) { - GXF_LOG_ERROR("Triton Server is unexpected"); - return nvidia::gxf::ToResultCode(maybe_model_repo); - } - auto model_repo = server_handle_.try_get().value()->getModelRepoConfig().value(); - - inference_config.mutable_backend()->mutable_triton()-> - mutable_model_repo()->CopyFrom(*model_repo); - - // suggests memory output storage location to Triton - if (output_storage_type_.try_get()) { - auto output_mem_type = GxfMemTypeToNvDsConfMemType( - nvidia::gxf::MemoryStorageType(output_storage_type_.try_get().value())); - inference_config.mutable_backend()->set_output_mem_type(output_mem_type); - } - - if (use_string_data_.get() || use_sequence_data_.get()) { - infer_context_.reset(createInferTritonSimpleContext()); - } else { - std::string configStr = inference_config.DebugString(); - infer_context_.reset(createInferTrtISContext(configStr.c_str(), configStr.size())); - } - - } else if (inference_mode_.get() == TritonInferenceMode::kRemoteGrpc) { - if (!server_endpoint_.try_get()) { - GXF_LOG_ERROR("Remote endpoint is not set with RemoteGrpc inference mode"); - return GXF_ARGUMENT_INVALID; - } - inference_config.mutable_backend()->mutable_triton()-> - mutable_grpc()->set_url(server_endpoint_.try_get().value()); - - // suggests memory output storage location to Triton - if (output_storage_type_.try_get()) { - auto output_mem_type = GxfMemTypeToNvDsConfMemType( - nvidia::gxf::MemoryStorageType(output_storage_type_.try_get().value())); - inference_config.mutable_backend()->set_output_mem_type(output_mem_type); - } - - // consider adding -> enable_cuda_buffer_sharing, when the DS team enables it - - if (use_string_data_.get() || use_sequence_data_.get()) { - infer_context_.reset(createInferTritonSimpleContext()); - } else { - std::string configStr = inference_config.DebugString(); - infer_context_.reset(createInferTritonGrpcContext(configStr.c_str(), configStr.size())); - } - } else { - GXF_LOG_ERROR("Invalid inference mode"); - return GXF_ARGUMENT_INVALID; - } - - if (!infer_context_) { - GXF_LOG_ERROR("Failure to create Inference Context for '%s'", model_name_.get().c_str()); - return GXF_FAILURE; - } - - NvDsInferStatus status = NVDSINFER_SUCCESS; - status = infer_context_->initialize(inference_config.DebugString(), nullptr); - - if (status != NVDSINFER_SUCCESS) { - GXF_LOG_ERROR("Failure to initialize Inference Context for '%s'", model_name_.get().c_str()); - return GXF_FAILURE; - } - return GXF_SUCCESS; -} - -gxf_result_t TritonInferencerImpl::destruct() { - for (auto& inference_ptr : inference_pool_) { - delete inference_ptr; - } - return GXF_SUCCESS; -} - -gxf_result_t TritonInferencerImpl::inferAsync( - const std::vector input_entities, - const std::vector input_names) { - // Reset scheduling term event to wait for next response. - scheduling_term_->setEventState(nvidia::gxf::AsynchronousEventState::EVENT_WAITING); - - // Use the current inference index to modify the inference object in the inference callback. - size_t current_inference_index = next_inference_index_.load(); - auto& inference = inference_pool_[current_inference_index]; - if (inference->is_active) { - GXF_LOG_ERROR("Next available Inference Context for '%s' is active. " - "Increase num_concurrent_requests.", model_name_.get().c_str()); - return GXF_EXCEEDING_PREALLOCATED_SIZE; - } - - next_inference_index_ = (next_inference_index_ + 1) % num_concurrent_requests_.get(); - - if (!infer_context_) { - GXF_LOG_ERROR("Inference Context not initialized"); - return GXF_FAILURE; - } - - // NOTE: Triton will own the input entity data until inference is complete. - // This is released during getResponse(). - inference->preserved_input = input_entities; - - nvdsinferserver::SharedIBatchArray input = NvDsInferServerCreateBatchArray(); - if (!input) { - GXF_LOG_ERROR("Unable to create nvds input tensors"); - return GXF_FAILURE; - } - - if (input_entities.size() != input_names.size()) { - GXF_LOG_ERROR("Mismatch in number of input_entities and input_names"); - return GXF_FAILURE; - } - - for (size_t i = 0; i < input_entities.size(); i++) { - auto input_entity = input_entities[i]; - auto input_name = input_names[i]; - auto maybe_entity_clone = input_entity.clone(); - if (!maybe_entity_clone) { - GXF_LOG_ERROR("Unable to clone input entity"); - return nvidia::gxf::ToResultCode(maybe_entity_clone); - } - auto entity_clone = maybe_entity_clone.value(); - inference->preserved_input.push_back(entity_clone); - auto input_tensors = entity_clone.findAll().value(); - for (auto input_tensor : input_tensors) { - const auto tensor = input_tensor.value(); - const auto& name = input_name.c_str(); - GXF_LOG_DEBUG("input tensor name = %s", name); - - if (tensor->rank() > NVDSINFER_MAX_DIMS) { - GXF_LOG_ERROR("Tensor rank '%u' is larger than NVDSINFER_MAX_DIMS"); - return GXF_FAILURE; - } - - // Input tensor needs to be fully batched - uint32_t batch_size = 0; // Offload "batch" to the fully specified InferDims instead - nvdsinferserver::InferDims dims; - - auto dataType = GxfToNvDsDataType(tensor->element_type()); - if (dataType == nvdsinferserver::InferDataType::kString) { - auto maybe_string_shape = input_entity.get(); - if (!maybe_string_shape) { - GXF_LOG_ERROR("Found Tensor with String Datatype "\ - "but no accompanying shape specification: "\ - "%s", name); - return GXF_FAILURE; - } - const auto shape = *maybe_string_shape.value(); - dims.numDims = shape.rank(); - dims.numElements = shape.size(); - for (size_t index = 0; index < shape.rank(); index++) { - if (shape.dimension(index) <= 0) { - GXF_LOG_ERROR("Tensor Dimension <= 0 not allowed"); - return GXF_FAILURE; - } - dims.d[index] = shape.dimension(index); - } - } else { - dims.numDims = tensor->rank(); - dims.numElements = static_cast(tensor->element_count()); - for (size_t index = 0; index < tensor->rank(); index++) { - if (tensor->shape().dimension(index) <= 0) { - GXF_LOG_ERROR("Tensor Dimension <= 0 not allowed"); - return GXF_FAILURE; - } - dims.d[index] = tensor->shape().dimension(index); - } - } - - nvdsinferserver::InferBufferDescription description { - memType : GxfMemTypeToNvDsMemType(tensor->storage_type()), - devId : 0, /* NOTE: GXF Allocator does not have concept of device ID for kGPU */ - dataType : dataType, - dims : dims, - elementSize : static_cast(tensor->bytes_per_element()), - name : name, - isInput : true - }; - - auto buffer = NvDsInferServerWrapBuf( - tensor->pointer(), tensor->size(), description, batch_size, [](void* data) {}); - input->appendIBatchBuf(buffer); - } - } - - auto maybe_triton_option = inference-> - preserved_input.front().get(); - if (maybe_triton_option) { - auto result = setTritonOptions(input, *maybe_triton_option.value()); - if (result != GXF_SUCCESS) { - return result; - } - } - - // Create a promise to be used in the inference callback - inference->response_promise = std::move(std::promise()); - - // Create a future object to be set in the inference callback - inference->response_completion = std::move(std::future( - inference->response_promise.get_future())); - - inference->infer_status = NVDSINFER_SUCCESS; - inference->is_active = true; - - // Increase count to be decremented once inference response is received. - incomplete_inference_count_++; - - NvDsInferStatus runStatus = infer_context_->run( - input, [current_inference_index, this]( - NvDsInferStatus s, nvdsinferserver::SharedIBatchArray out) { - auto& inference = inference_pool_[current_inference_index]; - inference->infer_status = s; - inference->raw_output = std::move(out); - inference->is_complete = true; - - if (scheduling_term_->getEventState() == nvidia::gxf::AsynchronousEventState::EVENT_DONE) { - GXF_LOG_DEBUG("Triton Async Event is unexpectedly already marked DONE"); - } - scheduling_term_->setEventState(nvidia::gxf::AsynchronousEventState::EVENT_DONE); - GXF_LOG_DEBUG("Triton Async Event DONE for index = %zu", current_inference_index); - - // NOTE: Set response promise last so that the EVENT_DONE notification occurs before the - // response wait() unblocks. This is to prevent a situation where EVENT_DONE is triggered - // after the entire response has already been processed. - inference->response_promise.set_value(); - }); - - if (runStatus != NVDSINFER_SUCCESS) { - GXF_LOG_ERROR("Unable to run Inference Context"); - return GXF_FAILURE; - } - return GXF_SUCCESS; -} - - -nvidia::gxf::Expected TritonInferencerImpl::getResponse() { - if (!infer_context_) { - GXF_LOG_ERROR("InferenceContext not initialized"); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - - auto& inference = inference_pool_[active_inference_index_]; - - if (!inference->is_complete) { - GXF_LOG_WARNING("Incomplete inference; response appeared out of order; invalid: index = %zu", - active_inference_index_); - GXF_LOG_WARNING("Inference appeared out of order"); - } - GXF_LOG_DEBUG("Trying to load inference for index: %zu", active_inference_index_); - - // Ensure the inference to complete. This normally will not block since the shared state - // should already be ready if this tick has been scheduled; however, if the inference response is - // received out of order from the inference request, we will wait to enforce FIFO. - inference->response_completion.wait(); - if (!inference->response_completion.valid() || !inference->is_active.load()) { - GXF_LOG_ERROR("Unexpectedly incomplete response for index: %zu", active_inference_index_); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - GXF_LOG_DEBUG("Successfully loaded inference for: %zu", active_inference_index_); - - // Increment the active index for the next response. - active_inference_index_ = (active_inference_index_ + 1) % num_concurrent_requests_.get(); - - if (inference->infer_status != NVDSINFER_SUCCESS) { - GXF_LOG_ERROR("Error with NvDs Async Infer: %d", inference->infer_status); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - - if (!inference->raw_output) { - GXF_LOG_ERROR("Unable to get valid outputs from Inference"); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - - auto maybe_output_entity = nvidia::gxf::Entity::New(context_); - if (!maybe_output_entity) { - GXF_LOG_ERROR("Unable to create maybe_output_entity"); - return maybe_output_entity; - } - - auto maybe_input_timestamp = inference-> - preserved_input.front().get("timestamp"); - if (maybe_input_timestamp) { - auto maybe_output_timestamp = - maybe_output_entity.value().add("timestamp"); - if (!maybe_output_timestamp) { - GXF_LOG_ERROR("Unable to create maybe_output_timestamp"); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - - *(maybe_output_timestamp.value().get()) = *(maybe_input_timestamp.value().get()); - } - - auto maybe_input_triton_option = inference-> - preserved_input.front().get(); - if (maybe_input_triton_option) { - auto maybe_output_triton_option = - maybe_output_entity.value().add(); - if (!maybe_output_triton_option) { - GXF_LOG_ERROR("Unable to create maybe_output_triton_option"); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - - *(maybe_output_triton_option.value().get()) = *(maybe_input_triton_option.value().get()); - } - - // Release the ref-counted input entity - inference->preserved_input.clear(); - auto& nvds_output = inference->raw_output; - - GXF_LOG_DEBUG("Raw Outputs size = %u", inference->raw_output->getSize()); - - for (uint32_t index = 0; index < inference->raw_output->getSize(); index++) { - const nvdsinferserver::IBatchBuffer* output_buf = inference->raw_output->getBuffer(index); - nvdsinferserver::SharedIBatchBuffer output_safe_buf = inference->raw_output->getSafeBuf(index); - if (!output_buf || output_safe_buf.get() != output_buf) { - GXF_LOG_ERROR("Mismatch between safe buffer and regular buffer of NvDs"); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - - const nvdsinferserver::InferBufferDescription& description = output_buf->getBufDesc(); - GXF_LOG_DEBUG("Batch size output '%s' = %u", - description.name.c_str(), output_buf->getBatchSize()); - GXF_LOG_DEBUG("Raw Outputs MemType type = %u", output_buf->getBufDesc().memType); - - auto tensor = maybe_output_entity.value().add(description.name.c_str()); - if (!tensor) { - GXF_LOG_ERROR("Unable to add tensor '%s' to output", description.name.c_str()); - return nvidia::gxf::Unexpected{tensor.error()}; - } - std::array dims; - dims[0] = output_buf->getBatchSize(); - size_t gxf_dims_index = 1; - uint32_t rank = 1; - - // If the model is non-dynamic, then batch size = 0. In this case, we need to ignore - // that dimension and override it with a meaningful dimension from DS's dimension. Reset rank - // to 0 as well to ignore previously set dimension. - if (dims[0] == 0) { - gxf_dims_index = 0; - rank = 0; - } - - // Batch will be first index in outgoing GXF Tensor - GXF_ASSERT_LE(description.dims.numDims + 1, nvidia::gxf::Shape::kMaxRank); - for (size_t nvsd_dim_index = 0; nvsd_dim_index < description.dims.numDims && - gxf_dims_index < nvidia::gxf::Shape::kMaxRank; nvsd_dim_index++, gxf_dims_index++) { - dims[gxf_dims_index] = static_cast(description.dims.d[nvsd_dim_index]); - rank++; - } - - nvidia::gxf::Shape ds_tensor_shape {dims, rank}; - uint64_t bytes_per_element = description.elementSize; - - if (description.dataType == nvdsinferserver::InferDataType::kString) { - GXF_LOG_DEBUG("Found output type of data type String!"); - // The shape returned by the inference for a string will be the shape of the unserialized - // data, which we preserve by adding to another component on the published entity. - auto maybe_output_shape = maybe_output_entity.value().add( - description.name.c_str()); - if (!maybe_output_shape) { - GXF_LOG_ERROR("Unable to add Shape '%s' to output", description.name.c_str()); - return nvidia::gxf::Unexpected{tensor.error()}; - } - *(maybe_output_shape.value().get()) = ds_tensor_shape; - - // Batch dimension does not matter here since serialization of strings is unique; it - // should only be interpreted with helper functions. We override the shape that is used for - // the outgoing tensor since we need to represent the fully serialized byte size. - ds_tensor_shape = nvidia::gxf::Shape{ - static_cast(output_buf->getTotalBytes())}; - bytes_per_element = 1; // sizeof(char) - } - - nvidia::gxf::MemoryStorageType target_storage_type = - NvDsMemTypeToGxfMemType(description.memType); - void* buffer_pointer = output_buf->getBufPtr(0); - - // convert output tensor to requested storage type - // if not specified in the config default, - // do not copy (ie. take whatever Triton gives as output) - bool needs_memory = false; - auto memcpy_kind = cudaMemcpyDefault; - if (output_storage_type_.try_get()) { - target_storage_type = nvidia::gxf::MemoryStorageType(output_storage_type_.try_get().value()); - auto current_storage_type = NvDsMemTypeToGxfMemType(description.memType); - if (target_storage_type != current_storage_type) { - switch (current_storage_type) { - case nvidia::gxf::MemoryStorageType::kHost: { - switch (target_storage_type) { - case nvidia::gxf::MemoryStorageType::kDevice: { - needs_memory = true; - memcpy_kind = cudaMemcpyHostToDevice; - } break; - case nvidia::gxf:: MemoryStorageType::kSystem: { - needs_memory = true; - memcpy_kind = cudaMemcpyHostToHost; - } break; - default: - GXF_LOG_ERROR("Unknown target storage type '%s' for copy", target_storage_type); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - } break; - case nvidia::gxf::MemoryStorageType::kDevice: { - switch (target_storage_type) { - case nvidia::gxf::MemoryStorageType::kHost: { - needs_memory = true; - memcpy_kind = cudaMemcpyDeviceToHost; - } break; - case nvidia::gxf:: MemoryStorageType::kSystem: { - needs_memory = true; - memcpy_kind = cudaMemcpyDeviceToHost; - } break; - default: - GXF_LOG_ERROR("Unknown target storage type '%s' for copy", target_storage_type); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - } break; - case nvidia::gxf:: MemoryStorageType::kSystem: { - switch (target_storage_type) { - case nvidia::gxf::MemoryStorageType::kHost: { - memcpy_kind = cudaMemcpyHostToHost; - } break; - case nvidia::gxf::MemoryStorageType::kDevice: { - memcpy_kind = cudaMemcpyHostToDevice; - } break; - default: - GXF_LOG_ERROR("Unknown target storage type '%s' for copy", target_storage_type); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - } break; - default: - GXF_LOG_ERROR("Unknown current storage type '%s' for copy", current_storage_type); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - } - - // Allocate memory if needed - if (needs_memory) { - auto pool = allocator_.try_get(); - if (!pool) { - GXF_LOG_ERROR("Allocator must be set when the requested output storage type" - " does not match triton output"); - return nvidia::gxf::Unexpected{GXF_ENTITY_COMPONENT_NOT_FOUND}; - } - - auto result = tensor.value()->reshapeCustom(ds_tensor_shape, - NvDsToGxfDataType(description.dataType), - bytes_per_element, - nvidia::gxf::Unexpected{GXF_UNINITIALIZED_VALUE}, - target_storage_type, pool.value()); - if (!result) { return nvidia::gxf::ForwardError(result); } - buffer_pointer = static_cast(tensor.value()->pointer()); - } - - // Perform the datacopy - const cudaError_t copy_error = cudaMemcpy(buffer_pointer, - output_buf->getBufPtr(0), - output_buf->getTotalBytes(), - memcpy_kind); - if (copy_error != cudaSuccess) { - GXF_LOG_ERROR("cudaMemcpy error: %s \n", cudaGetErrorString(copy_error)); - return nvidia::gxf::Unexpected{GXF_FAILURE}; - } - } - - // If memory was not allocated by this inferencer, then wrap incoming memory - // from triton context - if (!needs_memory) { - // Pass NvDs output by value to copy underlying shared_ptr. Once each of the outputs GXF tensors - // reach 0 ref count, the underlying NvDs ptr will also be decremented. - auto result = tensor.value()->wrapMemory( - ds_tensor_shape, NvDsToGxfDataType(description.dataType), bytes_per_element, - nvidia::gxf::Unexpected{GXF_UNINITIALIZED_VALUE}, - target_storage_type, - buffer_pointer, - [nvds_output] (void *) { - return nvidia::gxf::Success; - }); - - if (!result) { - GXF_LOG_ERROR("Unable to reshape tensor '%s' to output", description.name.c_str()); - return nvidia::gxf::Unexpected{result.error()}; - } - } - - GXF_ASSERT(output_buf->getTotalBytes() == tensor.value()->size(), - "Mismatch in expected GXF Tensor byte size: %" PRIu64 " != %zu", - output_buf->getTotalBytes(), tensor.value()->size()); - } - - // Shared instances of NvDs output will be managed through callbacks with wrapMemory - inference->raw_output = nvdsinferserver::SharedIBatchArray(); - // Reset for next inference - inference->is_complete = false; - inference->is_active = false; - - // We have processed this inference, so decrement this count, and check if all responses have - // been processed. - incomplete_inference_count_--; - GXF_LOG_DEBUG("incomplete_inference_count_ = %zu", incomplete_inference_count_.load()); - if (!incomplete_inference_count_.load()) { - GXF_LOG_DEBUG("Last inference reached; setting Async state to WAIT"); - scheduling_term_->setEventState(nvidia::gxf::AsynchronousEventState::WAIT); - } - - return maybe_output_entity; -} - -nvidia::gxf::Expected TritonInferencerImpl::isAcceptingRequest() { - if (!inference_pool_.size()) { return false; } - - // If the next inference context in the pool is not active, then another inference request can - // be stored there - const auto& inference = inference_pool_[next_inference_index_.load()]; - return !inference->is_active; -} - -} // namespace triton -} // namespace nvidia diff --git a/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.hpp b/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.hpp deleted file mode 100644 index af6e07d..0000000 --- a/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_impl.hpp +++ /dev/null @@ -1,291 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_INFERENCERS_TRITON_INFERENCER_IMPL_HPP -#define NVIDIA_TRITON_INFERENCERS_TRITON_INFERENCER_IMPL_HPP - -#include -#include -#include -#include -#include -#include - -#include "gxf/core/component.hpp" -#include "gxf/core/entity.hpp" -#include "gxf/std/allocator.hpp" -#include "gxf/std/scheduling_terms.hpp" -#include "gxf/std/tensor.hpp" - -#include "triton_inferencer_interface.hpp" - -#include "extensions/triton/triton_server.hpp" - - -#include "cuda_runtime.h" - - -namespace nvidia { -namespace triton { - -/** - * @brief Struct to maintain members for Inference, such as input, outputs, status - * - * @details This maintains input Entity and raw output from the inference response. This class - * is intended to ease the use of an inference request pool. - * - */ -struct Inference; - -/** - * @brief Enumeration for inference mode - * - */ -enum struct TritonInferenceMode { - kDirect = 0, - kRemoteGrpc = 1, -}; - -/** - * @brief Triton Direct C API Implementation for inferencing. - * - */ -class TritonInferencerImpl : public nvidia::triton::TritonInferencerInterface { - public: - /** - * @brief Register parameters for usage with this component. - * - * @param registrar - * @return gxf_result_t - */ - gxf_result_t registerInterface(nvidia::gxf::Registrar* registrar) override { - nvidia::gxf::Expected result; - - result &= registrar->parameter(server_handle_, "server", - "Triton Server", - "Triton Server Handle", - nvidia::gxf::Registrar::NoDefaultParameter(), - GXF_PARAMETER_FLAGS_OPTIONAL); - - result &= registrar->parameter(model_name_, "model_name", - "Triton Model Name", - "Triton Model Name. Refer to Triton Model Repository."); - - result &= registrar->parameter(model_version_, "model_version", - "Triton Model Version", - "Triton Model Version. Refer to Triton Model Repository."); - - result &= registrar->parameter(max_batch_size_, "max_batch_size", - "Triton Max Batch Size for Model", - "Triton Max Batch Size for Model, which should match Triton Model Repository."); - - result &= registrar->parameter(num_concurrent_requests_, - "num_concurrent_requests", - "Maximum Number of concurrent Inference Requests", - "Maximum Number of concurrent Inference Requests, which defines the pool.", - 1U); - - result &= registrar->parameter(allocator_, - "allocator", - "Allocator", - "Allocator instance for output tensors.", - nvidia::gxf::Registrar::NoDefaultParameter(), GXF_PARAMETER_FLAGS_OPTIONAL); - - result &= registrar->parameter(output_storage_type_, - "output_storage_type", - "Specified output memory location: kHost, kDevice, kSystem" \ - "The memory storage type used by this allocator. ", - "Can be kHost (0), kDevice (1) or kSystem (2)", - nvidia::gxf::Registrar::NoDefaultParameter(), GXF_PARAMETER_FLAGS_OPTIONAL); - - result &= registrar->parameter(use_string_data_, - "use_string_data", - "Specify whether string data is being sent to Triton", - "Specify whether string data is being sent to Triton", - false); - - result &= registrar->parameter(use_sequence_data_, - "use_sequence_data", - "Specify whether sequence data is being sent to Triton", - "Specify whether sequence data is being sent to Triton", - false); - - result &= registrar->parameter(scheduling_term_, "async_scheduling_term", - "Asynchronous Scheduling Term", "Asynchronous Scheduling Term"); - - result &= registrar->parameter(inference_mode_, - "inference_mode", - "Triton Inference mode: Direct, RemoteGrpc", - "Triton Inference mode: Direct, RemoteGrpc"); - - result &= registrar->parameter(server_endpoint_, - "server_endpoint", - "Triton Server Endpoint for GRPC or HTTP", - "Triton Server Endpoint for GRPC or HTTP", - nvidia::gxf::Registrar::NoDefaultParameter(), - GXF_PARAMETER_FLAGS_OPTIONAL); - - return nvidia::gxf::ToResultCode(result); - } - - gxf_result_t initialize() override; - - /** - * @brief Allocate Triton ResponseAllocator. Reserve space for Inference Pool. - * Create Inference Request Pool - * - * @return gxf_result_t - */ - gxf_result_t construct() override; - - /** - * @brief Deallocate Triton ResponseAllocator. Clear Inference Pool. - * - * @return gxf_result_t - */ - gxf_result_t destruct() override; - - /** - * @brief Dispatch Triton inference request asynchronously. - * - * @param[in] input_entities Vector of input entities that contain the tensor data that - * correspond to Triton model inputs - * - * @param[in] input_names Vector of name strings for the tensors that - * correspond to Triton model inputs - * - * @return gxf_result_t - */ - gxf_result_t inferAsync(const std::vector input_entities, - const std::vector input_names) - override; - - /** - * @brief Get the Triton Response after an inference completes. - * - * @return nvidia::gxf::Expected - */ - nvidia::gxf::Expected getResponse() override; - - /** - * @brief Checks if inferencer can accept a new inference request. - * - * @return nvidia::gxf::Expected - */ - nvidia::gxf::Expected isAcceptingRequest() override; - - private: - nvidia::gxf::Parameter> server_handle_; - nvidia::gxf::Parameter model_name_; - nvidia::gxf::Parameter model_version_; - nvidia::gxf::Parameter max_batch_size_; - nvidia::gxf::Parameter num_concurrent_requests_; - nvidia::gxf::Parameter inference_mode_; - nvidia::gxf::Parameter server_endpoint_; - - // Special cases that aren't fully supported by the TRTIS backend yet - // These will need to use the simple case - nvidia::gxf::Parameter use_string_data_; - nvidia::gxf::Parameter use_sequence_data_; - - // Specify the output storage type - nvidia::gxf::Parameter output_storage_type_; - // Specify allocator incase memory needs to be allocated when the requested output storage type - // does not match the output memory type of the triton context - gxf::Parameter> allocator_; - - // Async Scheduling Term required to get/set event state. - nvidia::gxf::Parameter> - scheduling_term_; - - // Use a shared pointer to the server due to lack of guarantees on deinitialization order with - // Server component. - std::shared_ptr server_; - - // Instance of IInferContext can be used across multiple inferences of the same model. - std::shared_ptr infer_context_; - - // Set up a pool of inferences that manage Tensor inputs and response promises. The size of the - // pool must be large enough to accomodate multiple asynchronous requests, and it is controlled - // via parameter interface. - std::vector inference_pool_; - - // Mutex to protect counting in inference pool - std::mutex mutex_; - - // This represents the currently active index for the inference pool. Responses will use this - // index for the promises and future events. - size_t active_inference_index_ { 0 }; - - // This represents the next available index in the inference pool. This can be asynchronously - // accessed via isAcceptingRequest(). - std::atomic next_inference_index_ { 0 }; - - // This represents the number of incompleted inferences for the async - // continuation/termination conditions. This is incremented in a callback, and decremented when - // the inference response is received. - std::atomic incomplete_inference_count_ { 0 }; -}; - -} // namespace triton -} // namespace nvidia - -namespace nvidia { -namespace gxf { - -/** - * @brief Custom parameter parser for TritonInferenceMode - * - */ -template <> -struct ParameterParser<::nvidia::triton::TritonInferenceMode> { - static Expected<::nvidia::triton::TritonInferenceMode> Parse( - gxf_context_t context, gxf_uid_t component_uid, - const char* key, const YAML::Node& node, - const std::string& prefix) { - const std::string value = node.as(); - if (strcmp(value.c_str(), "Direct") == 0) { - return ::nvidia::triton::TritonInferenceMode::kDirect; - } - if (strcmp(value.c_str(), "RemoteGrpc") == 0) { - return ::nvidia::triton::TritonInferenceMode::kRemoteGrpc; - } - return ::nvidia::gxf::Unexpected{GXF_ARGUMENT_OUT_OF_RANGE}; - } -}; - -template<> -struct ParameterWrapper<::nvidia::triton::TritonInferenceMode> { - static Expected Wrap( - gxf_context_t context, - const ::nvidia::triton::TritonInferenceMode& value) { - std::string string_value; - if (value == ::nvidia::triton::TritonInferenceMode::kDirect) { - string_value = "Direct"; - } else if (value == ::nvidia::triton::TritonInferenceMode::kRemoteGrpc) { - string_value = "RemoteGrpc"; - } else { - return ::nvidia::gxf::Unexpected{GXF_ARGUMENT_OUT_OF_RANGE}; - } - return ParameterWrapper::Wrap(context, string_value); - } -}; - -} // namespace gxf -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_interface.hpp b/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_interface.hpp deleted file mode 100644 index c351a75..0000000 --- a/isaac_ros_triton/gxf/triton/inferencers/triton_inferencer_interface.hpp +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_INFERENCERS_TRITON_INFERENCER_INTERFACE_HPP -#define NVIDIA_TRITON_INFERENCERS_TRITON_INFERENCER_INTERFACE_HPP - -#include -#include - -#include "gxf/core/component.hpp" -#include "gxf/core/entity.hpp" - -namespace nvidia { -namespace triton { - -/** - * @brief Interface to wrap implementation of Triton inferencing. - * - */ -class TritonInferencerInterface : public nvidia::gxf::Component { - public: - /** - * @brief Prepare and set up any members specific to implementation. - * - * @details Derived component may prepare any implementation specific - * members/details here. We cannot leverage initialize() due to lack - * of guarantees on other component initializations. - * - * @return gxf_result_t - */ - virtual gxf_result_t construct() = 0; - - /** - * @brief Destroy any members specific to implementation. - * - * @details Derived component may prepare any implementation specific - * members/details here. We cannot leverage deinitialize() due to lack - * of guarantees on other component initializations. - * - * @return gxf_result_t - */ - virtual gxf_result_t destruct() = 0; - - /** - * @brief Dispatch Triton inference request asynchronously. - * - * @param[in] tensors Entity that contains a tensor map with names - * corresponding to Triton model inputs - * - * @return gxf_result_t - */ - virtual gxf_result_t inferAsync(const std::vector input_entities, - const std::vector input_names) = 0; - - /** - * @brief Get the Triton Response after an inference completes. - * - * @return nvidia::gxf::Expected - */ - virtual nvidia::gxf::Expected getResponse() = 0; - - /** - * @brief Checks if inferencer can accept a new inference request. - * - * @details This will be leveraged by scheduling term that decides to - * schedule the request codelet. - * This allows for inferSync behavior depending upon inferencer's implementation. - * - * @return nvidia::gxf::Expected - */ - virtual nvidia::gxf::Expected isAcceptingRequest() = 0; -}; - -} // namespace triton -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbuf_fdmap.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbuf_fdmap.so deleted file mode 100755 index e66376b..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbuf_fdmap.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8fbdc0e54bdcb7266aacbe2cb1a3ea8f2f8a60f45feccc94afeffa05130e54c2 -size 13144 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurface.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurface.so deleted file mode 100755 index 4871bdb..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurface.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9f02d6cf91551377c2388011b8af31b0d659315df7727b3bc4413faaaf4070ee -size 688648 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurftransform.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurftransform.so deleted file mode 100755 index ee7457b..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvbufsurftransform.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2dba4b0fa4f0d2a22ddab5b19fe34e7ebbd566c636631840d6ea3878ce2b86d9 -size 23005768 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_infer_server.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_infer_server.so deleted file mode 100755 index 38d475e..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_infer_server.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:82c8eb2b7e666f63015f4235edc819d709fed850e2cdb30ee9910eb87c780285 -size 7722944 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferlogger.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferlogger.so deleted file mode 100755 index f77e8e1..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferlogger.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c6e74f0c2f8aab1fff93b2998a4a300daf92799077b0a1404ec41bd0d6ee377a -size 14312 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferutils.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferutils.so deleted file mode 100755 index 541077d..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_jetpack502/libnvds_inferutils.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:de706e13e20882b7a3df32a6ad2c14817c418c27c57f54904f3124f5f2b21721 -size 100768 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbuf_fdmap.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbuf_fdmap.so deleted file mode 100755 index 40f2267..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbuf_fdmap.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:67699773596e1b5273478e5d1dad6570643ae0fb4fc3f9a8a2bdf73af015ed93 -size 23560 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurface.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurface.so deleted file mode 100755 index cef46dc..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurface.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f50f7409918603b0b1728d38f4ace3839580b00b292937595a437e973540d0e1 -size 35160 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurftransform.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurftransform.so deleted file mode 100755 index f00c6b4..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvbufsurftransform.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a1d583f66519a0a148ff10f9a33f1eaf56b246fbee1d55a77f5b9efde71f4dcc -size 88543584 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_infer_server.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_infer_server.so deleted file mode 100644 index 69a0a28..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_infer_server.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3070aa1751ebc74922015d1aa76313000dcb225f2047c049f6903c1424934c37 -size 15256528 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferlogger.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferlogger.so deleted file mode 100755 index 6c59752..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferlogger.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9ceeb966a8c4ca53357535e696c1e77d0ce7cdea7f92467e3db700275e8f2ba1 -size 22928 diff --git a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferutils.so b/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferutils.so deleted file mode 100755 index d3714f3..0000000 --- a/isaac_ros_triton/gxf/triton/nvds/lib/gxf_x86_64_cuda_11_8/libnvds_inferutils.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3f06cbf5f6a099f8b9a0fc0f6a9f563a4b63f1cb739a64b47e555059308e1602 -size 150552 diff --git a/isaac_ros_triton/gxf/triton/triton_ext.cpp b/isaac_ros_triton/gxf/triton/triton_ext.cpp deleted file mode 100644 index 6530a1d..0000000 --- a/isaac_ros_triton/gxf/triton/triton_ext.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "gxf/core/component.hpp" -#include "gxf/std/codelet.hpp" -#include "gxf/std/extension_factory_helper.hpp" -#include "gxf/std/scheduling_term.hpp" - -#include "inferencers/triton_inferencer_impl.hpp" -#include "inferencers/triton_inferencer_interface.hpp" - -#include "triton_inference_request.hpp" -#include "triton_inference_response.hpp" -#include "triton_options.hpp" -#include "triton_scheduling_terms.hpp" -#include "triton_server.hpp" - - -GXF_EXT_FACTORY_BEGIN() - GXF_EXT_FACTORY_SET_INFO(0xa3c95d1cc06c4a4e, 0xa2f98d9078ab645c, - "NvTritonExt", - "Nvidia Triton Inferencing and Utilities Extension: 2.26.0 (x86_64), " - "2.30.0 (L4T - Jetpack 5.1)", - "NVIDIA", - "0.1.0", "LICENSE"); - GXF_EXT_FACTORY_ADD(0x26228984ffc44162, 0x9af56e3008aa2982, - nvidia::triton::TritonServer, - nvidia::gxf::Component, - "Triton Server Component for Direct Inference."); - GXF_EXT_FACTORY_ADD(0x1661c0156b1c422d, 0xa6f0248cdc197b1a, - nvidia::triton::TritonInferencerInterface, - nvidia::gxf::Component, - "Triton Inferencer Interface where specific Direct, Remote " - "or IPC inferencers can implement."); - GXF_EXT_FACTORY_ADD(0xb84cf267b2234df5, 0xac82752d9fae1014, - nvidia::triton::TritonInferencerImpl, - nvidia::triton::TritonInferencerInterface, - "Triton Inferencer that uses the Triton C API. Requires " - "Triton Server Component."); - GXF_EXT_FACTORY_ADD(0x34395920232c446f, 0xb5b746f642ce84df, - nvidia::triton::TritonInferenceRequest, - nvidia::gxf::Codelet, - "Triton Inference Request Codelet that wraps Triton Implementation."); - GXF_EXT_FACTORY_ADD(0x4dd957a7aa554117, 0x90d39a98e31ee176, - nvidia::triton::TritonInferenceResponse, - nvidia::gxf::Codelet, - "Triton Inference Response Codelet that wraps Triton Implementation."); - GXF_EXT_FACTORY_ADD_0(0x087696ed229d4199, 0x876f05b92d3887f0, - nvidia::triton::TritonOptions, - "Triton Inference Options for model control and sequence control."); - GXF_EXT_FACTORY_ADD(0xf860241212424e43, 0x9dbf9c559d496b84, - nvidia::triton::TritonRequestReceptiveSchedulingTerm, - nvidia::gxf::SchedulingTerm, - "Triton Scheduling Term that schedules Request Codelet when the inferencer " - "can accept a new request."); -GXF_EXT_FACTORY_END() diff --git a/isaac_ros_triton/gxf/triton/triton_inference_request.cpp b/isaac_ros_triton/gxf/triton/triton_inference_request.cpp deleted file mode 100644 index 62e4670..0000000 --- a/isaac_ros_triton/gxf/triton/triton_inference_request.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include -#include -#include - -#include "gxf/std/tensor.hpp" -#include "gxf/std/timestamp.hpp" - -#include "triton_inference_request.hpp" -#include "triton_options.hpp" - -namespace nvidia { -namespace triton { - -gxf_result_t TritonInferenceRequest::start() { - auto result = inferencer_.get()->construct(); - if (input_tensor_names_.get().size() == 0) { - GXF_LOG_ERROR("At least one input tensor is needed."); - return GXF_FAILURE; - } - if (input_tensor_names_.get().size() != input_binding_names_.get().size()) { - GXF_LOG_ERROR("Mismatching number of input tensor names and bindings: %lu vs %lu", - input_tensor_names_.get().size(), input_binding_names_.get().size()); - return GXF_FAILURE; - } - if (input_tensor_names_.get().size() != rx_.get().size()) { - GXF_LOG_ERROR("Mismatching number of input tensor names and receivers: %lu vs %lu", - input_tensor_names_.get().size(), rx_.get().size()); - return GXF_FAILURE; - } - if (rx_.get().size() == 0) { - GXF_LOG_ERROR("At least one receiver is needed."); - return GXF_FAILURE; - } - return result; -} - -gxf_result_t TritonInferenceRequest::tick() { - // Create a new entity that will serve as a tensor map for the model inputs - auto inputs_tensor_map = nvidia::gxf::Entity::New(context()); - if (!inputs_tensor_map) { - return nvidia::gxf::ToResultCode(inputs_tensor_map); - } - - auto& receivers = rx_.get(); - auto& binding_names = input_binding_names_.get(); - auto& tensor_names = input_tensor_names_.get(); - - std::vector input_entities; - std::vector input_names; - - auto maybe_output_timestamp = inputs_tensor_map.value().add("timestamp"); - if (!maybe_output_timestamp) { - return nvidia::gxf::ToResultCode(maybe_output_timestamp); - } - - for (size_t input_index = 0; input_index < receivers.size(); input_index++) { - auto& receiver = receivers[input_index]; - auto maybe_message = receiver->receive(); - if (!maybe_message) { - return nvidia::gxf::ToResultCode(maybe_message); - } - - // ensure entity includes tensor as input - auto& tensor_name = tensor_names[input_index]; - auto maybe_tensor_incoming = maybe_message.value().get( - tensor_name.c_str()); - if (!maybe_tensor_incoming) { - GXF_LOG_ERROR("Unable to find Tensor with name '%s'", tensor_name.c_str()); - return nvidia::gxf::ToResultCode(maybe_tensor_incoming); - } - - input_entities.push_back(maybe_message.value()); - input_names.push_back(binding_names[input_index]); - } - - return inferencer_.get()->inferAsync(input_entities, input_names); -} - -gxf_result_t TritonInferenceRequest::stop() { - auto result = inferencer_.get()->destruct(); - return result; -} - -} // namespace triton -} // namespace nvidia diff --git a/isaac_ros_triton/gxf/triton/triton_inference_request.hpp b/isaac_ros_triton/gxf/triton/triton_inference_request.hpp deleted file mode 100644 index 356e746..0000000 --- a/isaac_ros_triton/gxf/triton/triton_inference_request.hpp +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_TRITON_INFERENCE_REQUEST_HPP -#define NVIDIA_TRITON_TRITON_INFERENCE_REQUEST_HPP - -#include -#include - -#include "gxf/core/entity.hpp" -#include "gxf/core/expected.hpp" -#include "gxf/core/handle.hpp" -#include "gxf/std/codelet.hpp" -#include "gxf/std/parameter_parser_std.hpp" -#include "gxf/std/receiver.hpp" - -#include "inferencers/triton_inferencer_interface.hpp" - -namespace nvidia { -namespace triton { - -/** - * @brief Triton Inference Request that wraps generic TritonInferencer implementation. - * - * @details The Entity which holds this Codelet must also have TritonModelInput(s). - * - */ -class TritonInferenceRequest : public nvidia::gxf::Codelet { - public: - /** - * @brief Register Parameters - * - * @param registrar - * @return gxf_result_t - */ - gxf_result_t registerInterface(nvidia::gxf::Registrar* registrar) override { - nvidia::gxf::Expected result; - - result &= registrar->parameter(inferencer_, "inferencer", - "Inferencer Implementation", - "TritonInferenceInterface Inferencer Implementation Handle"); - result &= registrar->parameter(rx_, "rx", "Receivers", - "List of receivers to take input tensors"); - result &= registrar->parameter(input_tensor_names_, "input_tensor_names", "Input Tensor Names", - "Names of input tensors that exist in the ordered receivers in 'rx'."); - result &= registrar->parameter(input_binding_names_, "input_binding_names", - "Input Triton Binding Names", - "Names of input bindings corresponding to Triton's Config Inputs in the same order of " - "what is provided in 'input_tensor_names'."); - - return nvidia::gxf::ToResultCode(result); - } - - /** - * @brief Prepare TritonInferencerInterface - * - * @return gxf_result_t - */ - gxf_result_t start() override; - - /** - * @brief Receive tensors of TritonModelInput(s), create Tensor Map, submit - * inference request asynchronously. - * - * @return gxf_result_t - */ - gxf_result_t tick() override; - - /** - * @brief Destroys inferencer of type TritonInferencerInterface - * - * @return gxf_result_t - */ - gxf_result_t stop() override; - - private: - nvidia::gxf::Parameter> - inferencer_; - - nvidia::gxf::Parameter> input_tensor_names_; - nvidia::gxf::Parameter> input_binding_names_; - gxf::Parameter>> rx_; -}; - -} // namespace triton -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/gxf/triton/triton_inference_response.cpp b/isaac_ros_triton/gxf/triton/triton_inference_response.cpp deleted file mode 100644 index 2342fe6..0000000 --- a/isaac_ros_triton/gxf/triton/triton_inference_response.cpp +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include - -#include "gxf/std/tensor.hpp" -#include "gxf/std/timestamp.hpp" - -#include "triton_inference_response.hpp" -#include "triton_options.hpp" - -namespace nvidia { -namespace triton { - -gxf_result_t TritonInferenceResponse::start() { - if (!inferencer_.get()) { - GXF_LOG_ERROR("Inferencer unavailable"); - return GXF_FAILURE; - } - if (output_tensor_names_.get().size() == 0) { - GXF_LOG_ERROR("At least one output tensor is needed."); - return GXF_FAILURE; - } - if (output_tensor_names_.get().size() != output_binding_names_.get().size()) { - GXF_LOG_ERROR("Mismatching number of output tensor names and bindings: %lu vs %lu", - output_tensor_names_.get().size(), output_binding_names_.get().size()); - return GXF_FAILURE; - } - return GXF_SUCCESS; -} - -gxf_result_t TritonInferenceResponse::tick() { - // This getResponse() call is expected to be a blocking statement. - auto maybe_response = inferencer_.get()->getResponse(); - if (!maybe_response) { - return nvidia::gxf::ToResultCode(maybe_response); - } - - // Create a new entity for model output which will hold a Tensor Map - auto maybe_output_tensor_map = nvidia::gxf::Entity::New(context()); - if (!maybe_output_tensor_map) { - return nvidia::gxf::ToResultCode(maybe_output_tensor_map); - } - - auto& bindings = output_binding_names_.get(); - auto& tensors = output_tensor_names_.get(); - - // Implementation will return a tensor map with Triton Bindings. We need to translate that to the - // expected GXF Tensor names. - for (size_t output_index = 0; output_index < bindings.size(); output_index++) { - auto& tensor_name = tensors[output_index]; - auto maybe_tensor = maybe_output_tensor_map.value().add( - tensor_name.c_str()); - if (!maybe_tensor) { - return nvidia::gxf::ToResultCode(maybe_tensor); - } - - auto& triton_binding = bindings[output_index]; - auto maybe_response_tensor = maybe_response.value().get( - triton_binding.c_str()); - if (!maybe_response_tensor) { - GXF_LOG_ERROR("Unable to find tensor response for binding '%s'", triton_binding.c_str()); - return nvidia::gxf::ToResultCode(maybe_response_tensor); - } - - // Move incoming response tensor to the tensor that will be transmitted. There is no better way - // of redistributing tensors to varying sources with copy. - *(maybe_tensor.value().get()) = std::move(*(maybe_response_tensor.value().get())); - - // For String data, we need to publish nvidia::gxf::Shape so the serialized data can be - // interpreted correctly. - auto maybe_response_tensor_shape = maybe_response.value().get( - triton_binding.c_str()); - if (maybe_response_tensor_shape) { - auto maybe_shape = maybe_output_tensor_map.value().add( - tensor_name.c_str()); - if (!maybe_shape) { - return nvidia::gxf::ToResultCode(maybe_shape); - } - - *(maybe_shape.value().get()) = std::move(*(maybe_response_tensor_shape.value().get())); - } - } - - // Forward Triton Options so that consumer understands sequence id, end of sequence, etc. - auto maybe_input_triton_option = maybe_response.value().get(); - if (maybe_input_triton_option) { - auto maybe_output_triton_option = - maybe_output_tensor_map.value().add(); - if (!maybe_output_triton_option) { - return nvidia::gxf::ToResultCode(maybe_output_triton_option); - } - // Move incoming TritonOption from receiver to the outgoing tensor. - // NOTE: This modifies the incoming Entity tensor component via the move. - *(maybe_output_triton_option.value().get()) = - std::move(*(maybe_input_triton_option.value().get())); - } - - nvidia::gxf::Expected result = nvidia::gxf::Unexpected{GXF_FAILURE}; - - auto maybe_timestamp = maybe_response.value().get("timestamp"); - if (!maybe_timestamp) { - result = tx_.get()->publish(maybe_output_tensor_map.value()); - } else { - result = tx_.get()->publish(maybe_output_tensor_map.value(), maybe_timestamp.value()->acqtime); - } - - if (!result) { - GXF_LOG_ERROR("Error when transmitting message output tensor map"); - return nvidia::gxf::ToResultCode(result); - } - - return GXF_SUCCESS; -} - -gxf_result_t TritonInferenceResponse::stop() { - return GXF_SUCCESS; -} - -} // namespace triton -} // namespace nvidia diff --git a/isaac_ros_triton/gxf/triton/triton_inference_response.hpp b/isaac_ros_triton/gxf/triton/triton_inference_response.hpp deleted file mode 100644 index 70568f0..0000000 --- a/isaac_ros_triton/gxf/triton/triton_inference_response.hpp +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_TRITON_INFERENCE_RESPONSE_HPP -#define NVIDIA_TRITON_TRITON_INFERENCE_RESPONSE_HPP - -#include -#include - -#include "gxf/core/entity.hpp" -#include "gxf/core/expected.hpp" -#include "gxf/core/handle.hpp" -#include "gxf/std/codelet.hpp" -#include "gxf/std/transmitter.hpp" - -#include "inferencers/triton_inferencer_interface.hpp" - -namespace nvidia { -namespace triton { - -/** - * @brief Triton Inference Response that wraps generic TritonInferencer implementation. - * - * @details The Entity which holds this Codelet must also have TritonModelOutput(s). - * - */ -class TritonInferenceResponse : public nvidia::gxf::Codelet { - public: - /** - * @brief Register Parameters. - * - * @param registrar - * @return gxf_result_t - */ - gxf_result_t registerInterface(nvidia::gxf::Registrar* registrar) override { - nvidia::gxf::Expected result; - - result &= registrar->parameter(inferencer_, "inferencer", - "Inferencer Implementation", - "TritonInferenceInterface Inferencer Implementation Handle"); - result &= registrar->parameter(output_tensor_names_, "output_tensor_names", - "Output Tensor Names", - "Names of output tensors in the order to be retrieved from the model."); - result &= registrar->parameter(output_binding_names_, "output_binding_names", - "Output Binding Names", - "Names of output bindings in the model in the same " - "order of of what is provided in output_tensor_names."); - result &= registrar->parameter(tx_, "tx", "TX", "Transmitter to publish output tensors"); - return nvidia::gxf::ToResultCode(result); - } - - /** - * @brief Return success. - * - * @return gxf_result_t - */ - gxf_result_t start() override; - - /** - * @brief Gets Response from Inferencer and transmits output tensors respectively - * to TritonModelOutput(s) Transmitters - * - * @return gxf_result_t - */ - gxf_result_t tick() override; - - /** - * @brief Return success. - * - * @return gxf_result_t - */ - gxf_result_t stop() override; - - private: - nvidia::gxf::Parameter> - inferencer_; - - nvidia::gxf::Parameter> tx_; - nvidia::gxf::Parameter> output_tensor_names_; - nvidia::gxf::Parameter> output_binding_names_; -}; - -} // namespace triton -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/gxf/triton/triton_scheduling_terms.cpp b/isaac_ros_triton/gxf/triton/triton_scheduling_terms.cpp deleted file mode 100644 index 3380fb2..0000000 --- a/isaac_ros_triton/gxf/triton/triton_scheduling_terms.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#include "triton_scheduling_terms.hpp" - -namespace nvidia { -namespace triton { - -gxf_result_t TritonRequestReceptiveSchedulingTerm::initialize() { - if (!inferencer_.get()) { - GXF_LOG_ERROR("Inferencer unavailable"); - return GXF_FAILURE; - } - return GXF_SUCCESS; -} - -gxf_result_t TritonRequestReceptiveSchedulingTerm::check_abi(int64_t timestamp, - nvidia::gxf::SchedulingConditionType* type, int64_t* target_timestamp) const { - auto maybe_is_accepting_request = inferencer_.get()->isAcceptingRequest(); - if (!maybe_is_accepting_request) { - GXF_LOG_ERROR("Inference isAcceptingRequest had unexpected return"); - return GXF_FAILURE; - } - const auto& is_accepting_request = maybe_is_accepting_request.value(); - *type = is_accepting_request ? nvidia::gxf::SchedulingConditionType::READY : - nvidia::gxf::SchedulingConditionType::WAIT; - return GXF_SUCCESS; -} - -gxf_result_t TritonRequestReceptiveSchedulingTerm::onExecute_abi(int64_t dt) { - return GXF_SUCCESS; -} - -} // namespace triton -} // namespace nvidia diff --git a/isaac_ros_triton/gxf/triton/triton_scheduling_terms.hpp b/isaac_ros_triton/gxf/triton/triton_scheduling_terms.hpp deleted file mode 100644 index 87f2c19..0000000 --- a/isaac_ros_triton/gxf/triton/triton_scheduling_terms.hpp +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// SPDX-License-Identifier: Apache-2.0 - -#ifndef NVIDIA_TRITON_TRITON_SCHEDULING_TERMS_HPP -#define NVIDIA_TRITON_TRITON_SCHEDULING_TERMS_HPP - -#include -#include -#include - -#include "gxf/core/handle.hpp" -#include "gxf/std/scheduling_term.hpp" - -#include "inferencers/triton_inferencer_interface.hpp" - -namespace nvidia { -namespace triton { - -/** - * @brief Scheduling term which permits execution only when the Triton Request Component is able to - * accept new requests. - * - */ -class TritonRequestReceptiveSchedulingTerm : public nvidia::gxf::SchedulingTerm { - public: - /** - * @brief Register Parameters. - * - * @param registrar - * @return gxf_result_t - */ - gxf_result_t registerInterface(nvidia::gxf::Registrar* registrar) override { - nvidia::gxf::Expected result; - - result &= registrar->parameter(inferencer_, "inferencer", - "Inferencer Implementation", - "TritonInferenceInterface Inferencer Implementation Handle"); - return nvidia::gxf::ToResultCode(result); - } - - /** - * @brief Returns success - * - * @return gxf_result_t - */ - gxf_result_t initialize() override; - - /** - * @brief Only when an inferencer can accept new inference request, SchedulingCondition is - * Ready - * - * @param[in] timestamp Unused - * @param[out] type Ready (if inferencer can accept new inference request) or Wait (otherwise) - * @param[out] target_timestamp Unmodified - * @return gxf_result_t - */ - gxf_result_t check_abi(int64_t timestamp, nvidia::gxf::SchedulingConditionType* type, - int64_t* target_timestamp) const override; - - /** - * @brief Returns success - * - * @param dt Unused - * @return gxf_result_t - */ - gxf_result_t onExecute_abi(int64_t dt) override; - - private: - nvidia::gxf::Parameter> - inferencer_; -}; - -} // namespace triton -} // namespace nvidia - -#endif diff --git a/isaac_ros_triton/include/isaac_ros_triton_node/triton_node.hpp b/isaac_ros_triton/include/isaac_ros_triton_node/triton_node.hpp index 7606a48..3ab9380 100644 --- a/isaac_ros_triton/include/isaac_ros_triton_node/triton_node.hpp +++ b/isaac_ros_triton/include/isaac_ros_triton_node/triton_node.hpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/launch/isaac_ros_triton.launch.py b/isaac_ros_triton/launch/isaac_ros_triton.launch.py index d8f3296..faccfd2 100644 --- a/isaac_ros_triton/launch/isaac_ros_triton.launch.py +++ b/isaac_ros_triton/launch/isaac_ros_triton.launch.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/package.xml b/isaac_ros_triton/package.xml index 15ba39f..90c3020 100644 --- a/isaac_ros_triton/package.xml +++ b/isaac_ros_triton/package.xml @@ -21,10 +21,10 @@ isaac_ros_triton - 2.1.0 + 3.0.0 DNN Inference support for Isaac ROS - CY Chen + Isaac ROS Maintainers Apache-2.0 https://developer.nvidia.com/isaac-ros-gems/ CY Chen @@ -39,6 +39,8 @@ isaac_ros_common + gxf_isaac_triton + ament_lint_auto ament_lint_common isaac_ros_test diff --git a/isaac_ros_triton/src/triton_node.cpp b/isaac_ros_triton/src/triton_node.cpp index ea36e3e..d4f2dcc 100644 --- a/isaac_ros_triton/src/triton_node.cpp +++ b/isaac_ros_triton/src/triton_node.cpp @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -// Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ const std::vector> EXTENSIONS = { {"isaac_ros_gxf", "gxf/lib/std/libgxf_std.so"}, {"isaac_ros_gxf", "gxf/lib/cuda/libgxf_cuda.so"}, {"isaac_ros_gxf", "gxf/lib/serialization/libgxf_serialization.so"}, - {"isaac_ros_triton", "gxf/triton/libgxf_triton_ext.so"} + {"gxf_isaac_triton", "gxf/lib/libgxf_isaac_triton.so"} }; const std::vector PRESET_EXTENSION_SPEC_NAMES = { "isaac_ros_triton", diff --git a/isaac_ros_triton/test/isaac_ros_triton_test_onnx.py b/isaac_ros_triton/test/isaac_ros_triton_test_onnx.py index 1ce059c..c189c26 100644 --- a/isaac_ros_triton/test/isaac_ros_triton_test_onnx.py +++ b/isaac_ros_triton/test/isaac_ros_triton_test_onnx.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/isaac_ros_triton/test/isaac_ros_triton_test_tf.py b/isaac_ros_triton/test/isaac_ros_triton_test_tf.py index 0018ddc..c1c4684 100644 --- a/isaac_ros_triton/test/isaac_ros_triton_test_tf.py +++ b/isaac_ros_triton/test/isaac_ros_triton_test_tf.py @@ -1,5 +1,5 @@ # SPDX-FileCopyrightText: NVIDIA CORPORATION & AFFILIATES -# Copyright (c) 2021-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License.