Skip to content

Commit a6c413d

Browse files
leungjchGongstaEdwardius
authored
Real time semantic segmentation node (#77)
* Clean up dockerfile and docker compose * Add space optimized prod image stage * Clean up code * Update image with correct header and create save image directories * Add visualization topics * Address PR comments, add perception_utils --------- Co-authored-by: Steven Gong <[email protected]> Co-authored-by: Eddy Zhou <[email protected]>
1 parent 4608997 commit a6c413d

File tree

14 files changed

+708
-102
lines changed

14 files changed

+708
-102
lines changed

docker/perception/semantic_segmentation/semantic_segmentation.Dockerfile

Lines changed: 36 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
ARG BASE_BUILD_IMAGE=ghcr.io/watonomous/wato_monorepo/base:cuda11.7-humble-ubuntu22.04-devel
2-
ARG BASE_PROD_IMAGE=ghcr.io/watonomous/wato_monorepo/base:cuda11.7-humble-ubuntu22.04
3-
ARG BASE_PYTORCH_IMAGE=ghcr.io/watonomous/wato_monorepo/segformer_segmentation:latest
4-
# ################################ Build library ################################
1+
ARG BASE_IMAGE=ghcr.io/watonomous/wato_monorepo/base:cuda12.0-humble-ubuntu22.04-devel
2+
ARG RUNTIME_IMAGE=ghcr.io/watonomous/wato_monorepo/base:cuda12.0-humble-ubuntu22.04
3+
ARG PADDLE_INFERENCE_BUILD_URL=ghcr.io/watonomous/perception_paddlepaddle_inference_build_cuda-12.0
4+
################################ Build library ################################
5+
FROM ${PADDLE_INFERENCE_BUILD_URL} as PADDLE_INFERENCE_BUILD
56

6-
FROM ${BASE_PYTORCH_IMAGE} as Segformer
7+
################################ Source ################################
8+
FROM ${BASE_IMAGE} as source
79

8-
# # ################################ Source ################################
9-
10-
11-
FROM ${BASE_BUILD_IMAGE} as source
1210
WORKDIR ${AMENT_WS}/src
1311

14-
# # Copy in source code
12+
# Copy in the paddle inference library
13+
RUN mkdir -p semantic_segmentation/src
14+
COPY --from=PADDLE_INFERENCE_BUILD /paddle/paddle_inference_cuda120_build.tar /paddle/paddle_inference_cuda120_build.tar
15+
RUN tar -xvf /paddle/paddle_inference_cuda120_build.tar -C /paddle/
16+
RUN rm /paddle/paddle_inference_cuda120_build.tar
17+
18+
# Copy in source code
1519
COPY src/perception/semantic_segmentation semantic_segmentation
16-
COPY src/wato_msgs/sample_msgs sample_msgs
17-
COPY --from=Segformer /mmsegmentation/model ${AMENT_WS}/src/semantic_segmentation/resource/model
20+
COPY src/perception/perception_utils perception_utils
1821

1922
# Scan for rosdeps
2023
RUN apt-get -qq update && rosdep update && \
@@ -25,43 +28,17 @@ RUN apt-get -qq update && rosdep update && \
2528

2629

2730
################################# Dependencies ################################
28-
FROM ${BASE_BUILD_IMAGE} as dependencies
31+
FROM ${BASE_IMAGE} as dependencies
2932

30-
RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub
31-
RUN apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub
32-
33-
# Install system dependencies
34-
RUN apt-get update && apt-get install -y \
35-
git \
36-
python3-pip \
37-
ninja-build \
38-
libglib2.0-0 \
39-
libsm6 \
40-
libxrender-dev \
41-
libxext6 \
42-
libgl1-mesa-dev \
43-
libopencv-dev \
44-
&& apt-get clean \
45-
&& rm -rf /var/lib/apt/lists/*
46-
47-
# Install Segformer dependencies
48-
# COPY --from=Segformer /tmp/pip_install_list.txt /tmp/pip_install_list.txt torchaudio==0.13.1
49-
RUN pip install torch==1.13.1+cu116 \
50-
torchvision==0.14.1+cu116 --extra-index-url https://download.pytorch.org/whl/cu116 \
51-
cython \
52-
https://download.openmmlab.com/mmcv/dist/cu117/torch1.13.0/mmcv-2.0.0rc4-cp310-cp310-manylinux1_x86_64.whl
53-
RUN apt update && apt install -y ros-humble-cv-bridge
54-
55-
WORKDIR /mmsegmentation
56-
COPY --from=Segformer /mmsegmentation /mmsegmentation
57-
RUN pip install -r requirements.txt --no-cache-dir -e . && pip uninstall numpy -y && pip install numpy==1.26.4
33+
RUN apt update && apt install -y tensorrt ros-humble-cv-bridge libopencv-dev
5834

5935
# Install Rosdep requirements
6036
COPY --from=source /tmp/colcon_install_list /tmp/colcon_install_list
6137
RUN apt install -qq -y --no-install-recommends $(cat /tmp/colcon_install_list)
6238

6339
# Copy in source code from source stage
6440
WORKDIR ${AMENT_WS}
41+
COPY --from=source /paddle /paddle
6542
COPY --from=source ${AMENT_WS}/src src
6643

6744
# Dependency Cleanup
@@ -72,10 +49,6 @@ RUN apt-get -qq autoremove -y && apt-get -qq autoclean && apt-get -qq clean && \
7249
################################ Build ################################
7350
FROM dependencies as build
7451

75-
76-
ENV FORCE_CUDA="1"
77-
78-
7952
# Build ROS2 packages
8053
WORKDIR ${AMENT_WS}
8154
RUN . /opt/ros/$ROS_DISTRO/setup.sh && \
@@ -86,30 +59,43 @@ RUN . /opt/ros/$ROS_DISTRO/setup.sh && \
8659
COPY docker/wato_ros_entrypoint.sh ${AMENT_WS}/wato_ros_entrypoint.sh
8760

8861
# Add runtime libraries to path
89-
ENV CUDNN_DIR=/mmsegmentation/cuda
90-
ENV CV2_CUDABACKEND=0
9162
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AMENT_WS}/install/semantic_segmentation/lib/
9263
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
9364

9465
ENTRYPOINT ["./wato_ros_entrypoint.sh"]
9566

96-
################################ Prod ################################
97-
FROM build as deploy
67+
# ################################ Prod ################################
68+
FROM ${RUNTIME_IMAGE} as deploy
69+
9870

9971
# Install runtime libs
10072
RUN apt-get update && apt-get install -y \
101-
ros-humble-cv-bridge
73+
ros-humble-cv-bridge \
74+
tensorrt
10275

76+
# Copy the compiled binary to the runtime image
77+
COPY --from=build ${AMENT_WS} ${AMENT_WS}
10378

10479
WORKDIR ${AMENT_WS}
10580

81+
# Copy in the paddle inference library
10682
RUN mkdir -p install/semantic_segmentation/lib/
83+
COPY --from=PADDLE_INFERENCE_BUILD /paddle/paddle_inference_cuda120_build.tar install/semantic_segmentation/lib/paddle_inference_cuda120_build.tar
84+
RUN tar -xvf install/semantic_segmentation/lib/paddle_inference_cuda120_build.tar -C install/semantic_segmentation/lib
85+
RUN rm install/semantic_segmentation/lib/paddle_inference_cuda120_build.tar
86+
10787
# Add runtime libraries to path
10888
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AMENT_WS}/install/semantic_segmentation/lib/
10989
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
90+
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AMENT_WS}/install/semantic_segmentation/lib/paddle_inference_cuda120_build/paddle_inference_install_dir/paddle/lib/
91+
92+
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AMENT_WS}/install/semantic_segmentation/lib/paddle_inference_cuda120_build/paddle_inference_install_dir/third_party/install/cryptopp/lib/
93+
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AMENT_WS}/install/semantic_segmentation/lib/paddle_inference_cuda120_build/paddle_inference_install_dir/third_party/install/mkldnn/lib/
94+
ENV LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${AMENT_WS}/install/semantic_segmentation/lib/paddle_inference_cuda120_build/paddle_inference_install_dir/third_party/install/mklml/lib/
11095

11196
COPY docker/wato_ros_entrypoint.sh ${AMENT_WS}/wato_ros_entrypoint.sh
11297
ENTRYPOINT ["./wato_ros_entrypoint.sh"]
98+
11399
# Source Cleanup and Security Setup
114100
RUN chown -R $USER:$USER ${AMENT_WS}
115101
RUN rm -rf src/*

modules/docker-compose.perception.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ services:
4747
command: /bin/bash -c "ros2 launch lidar_object_detection nuscenes_launch.py"
4848
volumes:
4949
- /mnt/wato-drive2/perception_models/transfusion_trained_model.pth:/home/bolty/OpenPCDet/models/transfusion_trained_model.pth
50+
5051
semantic_segmentation:
5152
build:
5253
context: ..
@@ -56,17 +57,16 @@ services:
5657
- "${PERCEPTION_SEMANTIC_SEGMENTATION_IMAGE}:build_main"
5758
target: deploy
5859
image: "${PERCEPTION_SEMANTIC_SEGMENTATION_IMAGE}:${TAG}"
59-
command: /bin/bash -c "ros2 launch semantic_segmentation semantic_segmentation.launch.py"
60-
volumes:
61-
- /mnt/wato-drive/perception/segformer-b2:/home/bolty/ament_ws/src/semantic_segmentation/resource/model
62-
# add gpus all
60+
command: /bin/bash -c "ros2 launch semantic_segmentation eve.launch.py"
6361
deploy:
6462
resources:
6563
reservations:
6664
devices:
6765
- driver: nvidia
68-
capabilities: [gpu]
6966
count: 1
67+
capabilities: [ gpu ]
68+
volumes:
69+
- /mnt/wato-drive2/perception-weights/semantic_segmentation/pp_liteseg_infer_model:/perception_models/semantic_segmentation/pp_liteseg_infer_model
7070

7171
lane_detection:
7272
build:
@@ -108,4 +108,4 @@ services:
108108
- "${PERCEPTION_DEPTH_ESTIMATION_IMAGE}:build_main"
109109
target: deploy
110110
image: "${PERCEPTION_DEPTH_ESTIMATION_IMAGE}:${TAG}"
111-
command: /bin/bash -c "ros2 launch depth_estimation eve.launch.py"
111+
command: /bin/bash -c "ros2 launch depth_estimation eve.launch.py"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
project(perception_utils)
3+
4+
# Default to C++14
5+
if(NOT CMAKE_CXX_STANDARD)
6+
set(CMAKE_CXX_STANDARD 14)
7+
endif()
8+
9+
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
10+
add_compile_options(-Wall -Wextra -Wpedantic)
11+
endif()
12+
13+
# Find dependencies
14+
find_package(ament_cmake REQUIRED)
15+
find_package(rclcpp REQUIRED)
16+
find_package(OpenCV REQUIRED)
17+
18+
# Include header files
19+
include_directories(include
20+
${OpenCV_INCLUDE_DIRS}
21+
)
22+
23+
# Declare a C++ library
24+
add_library(perception_utils SHARED
25+
src/camera_utils.cpp
26+
)
27+
28+
# Specify libraries to link a library or executable target against
29+
ament_target_dependencies(perception_utils
30+
rclcpp
31+
OpenCV)
32+
33+
install(TARGETS perception_utils
34+
EXPORT export_perception_utils
35+
ARCHIVE DESTINATION lib
36+
LIBRARY DESTINATION lib
37+
RUNTIME DESTINATION bin
38+
)
39+
40+
ament_export_targets(export_perception_utils HAS_LIBRARY_TARGET)
41+
ament_export_include_directories(include)
42+
ament_export_dependencies(rclcpp)
43+
44+
# Install header files
45+
install(DIRECTORY include/
46+
DESTINATION include
47+
)
48+
49+
ament_package()
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#ifndef CAMERA_UTILS_HPP_
2+
#define CAMERA_UTILS_HPP_
3+
4+
#include <opencv2/opencv.hpp>
5+
6+
namespace CameraUtils {
7+
// Swaps the channels of an image from HWC to CHW format
8+
void hwc_img_2_chw_data(const cv::Mat& hwc_img, float* data);
9+
cv::Mat resize_image_aspect_ratio(const cv::Mat& original_image, int max_width, int max_height);
10+
cv::Mat resize_with_padding(const cv::Mat& original_image, int target_width, int target_height);
11+
cv::Mat resize_from_center(const cv::Mat& original_image, int target_width, int target_height);
12+
}; // namespace CameraUtils
13+
14+
#endif // CAMERA_UTILS_HPP_
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0"?>
2+
<package format="3">
3+
<name>perception_utils</name>
4+
<version>0.0.1</version>
5+
<description>A utility library for perception tasks in ROS 2.</description>
6+
7+
<maintainer email="[email protected]">Justin Leung</maintainer>
8+
<license>Apache-2.0</license>
9+
10+
<buildtool_depend>ament_cmake</buildtool_depend>
11+
<depend>rclcpp</depend>
12+
<depend>opencv</depend>
13+
14+
<export>
15+
<build_type>ament_cmake</build_type>
16+
</export>
17+
18+
</package>
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#include "perception_utils/camera_utils.hpp"
2+
#include <opencv2/opencv.hpp>
3+
4+
// Swaps the channels of an image from HWC to CHW format
5+
void CameraUtils::hwc_img_2_chw_data(const cv::Mat& hwc_img, float* data) {
6+
int rows = hwc_img.rows;
7+
int cols = hwc_img.cols;
8+
int chs = hwc_img.channels();
9+
for (int i = 0; i < chs; ++i) {
10+
cv::extractChannel(hwc_img, cv::Mat(rows, cols, CV_32FC1, data + i * rows * cols), i);
11+
}
12+
}
13+
14+
cv::Mat CameraUtils::resize_image_aspect_ratio(const cv::Mat& original_image, int max_width,
15+
int max_height) {
16+
int original_width = original_image.cols;
17+
int original_height = original_image.rows;
18+
double original_aspect_ratio = (double)original_width / original_height;
19+
20+
int new_width, new_height;
21+
double max_aspect_ratio = (double)max_width / max_height;
22+
23+
if (original_aspect_ratio > max_aspect_ratio) {
24+
// Width is the limiting factor
25+
new_width = max_width;
26+
new_height = static_cast<int>(max_width / original_aspect_ratio);
27+
} else {
28+
// Height is the limiting factor
29+
new_height = max_height;
30+
new_width = static_cast<int>(max_height * original_aspect_ratio);
31+
}
32+
33+
cv::Mat resized_image;
34+
cv::resize(original_image, resized_image, cv::Size(new_width, new_height));
35+
36+
return resized_image;
37+
}
38+
39+
cv::Mat CameraUtils::resize_with_padding(const cv::Mat& original_image, int target_width,
40+
int target_height) {
41+
int original_width = original_image.cols;
42+
int original_height = original_image.rows;
43+
44+
double target_ratio = (double)target_width / target_height;
45+
double original_ratio = (double)original_width / original_height;
46+
47+
int new_width, new_height;
48+
49+
if (original_ratio > target_ratio) {
50+
// Original is wider. Fit to width and pad height.
51+
new_width = target_width;
52+
new_height =
53+
static_cast<int>(original_height * (static_cast<double>(target_width) / original_width));
54+
} else {
55+
// Original is taller. Fit to height and pad width.
56+
new_height = target_height;
57+
new_width =
58+
static_cast<int>(original_width * (static_cast<double>(target_height) / original_height));
59+
}
60+
61+
cv::Mat resized_image;
62+
cv::resize(original_image, resized_image, cv::Size(new_width, new_height));
63+
64+
int top = (target_height - new_height) / 2;
65+
int bottom = target_height - new_height - top;
66+
int left = (target_width - new_width) / 2;
67+
int right = target_width - new_width - left;
68+
69+
cv::Mat padded_image;
70+
cv::copyMakeBorder(resized_image, padded_image, top, bottom, left, right, cv::BORDER_CONSTANT,
71+
cv::Scalar(0, 0, 0));
72+
73+
return padded_image;
74+
}
75+
76+
cv::Mat CameraUtils::resize_from_center(const cv::Mat& original_image, int target_width,
77+
int target_height) {
78+
int original_width = original_image.cols;
79+
int original_height = original_image.rows;
80+
81+
// Calculate the new height maintaining the aspect ratio
82+
double target_ratio = (double)target_width / target_height;
83+
int new_height = static_cast<int>(original_width / target_ratio);
84+
85+
// Calculate the cropping area
86+
int startY = (original_height - new_height) / 2;
87+
88+
// Crop the image from the center
89+
cv::Rect roi(0, startY, original_width, new_height);
90+
cv::Mat cropped_image = original_image(roi);
91+
92+
// Resize the cropped image
93+
cv::Mat resized_image;
94+
cv::resize(cropped_image, resized_image, cv::Size(target_width, target_height));
95+
96+
return resized_image;
97+
}

0 commit comments

Comments
 (0)