-
Notifications
You must be signed in to change notification settings - Fork 237
Expand file tree
/
Copy pathpoint_cloud_processor.h
More file actions
157 lines (134 loc) · 6.46 KB
/
Copy pathpoint_cloud_processor.h
File metadata and controls
157 lines (134 loc) · 6.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* Copyright (c) 2018-2023, Ouster, Inc.
* All rights reserved.
*
* @file point_cloud_producer.h
* @brief takes in a lidar scan object and produces a PointCloud2 message
*/
#pragma once
// prevent clang-format from altering the location of "ouster_ros/ros.h", the
// header file needs to be the first include due to PCL_NO_PRECOMPILE flag
// clang-format off
#include "ouster_ros/os_ros.h"
// clang-format on
#include "point_cloud_compose.h"
#include "lidar_packet_handler.h"
#include "impl/cartesian.h"
#include <opencv2/opencv.hpp>
#include <opencv2/core/eigen.hpp>
namespace ouster_ros {
// Moved out of PointCloudProcessor to avoid type templatization
using PointCloudProcessor_OutputType =
std::vector<std::shared_ptr<sensor_msgs::PointCloud2>>;
using PointCloudProcessor_PostProcessingFn = std::function<void(PointCloudProcessor_OutputType)>;
template <class PointT>
class PointCloudProcessor {
public:
using ScanToCloudFn = std::function<void(ouster_ros::Cloud<PointT>& cloud,
const ouster::PointsF& points,
uint64_t scan_ts, const ouster::LidarScan& ls,
const std::vector<int>& pixel_shift_by_row,
int return_index)>;
public:
PointCloudProcessor(const ouster::sensor::sensor_info& info,
const std::string& frame_id,
bool apply_lidar_to_sensor_transform,
uint32_t min_range, uint32_t max_range, int rows_step,
const std::string& mask_path,
ScanToCloudFn scan_to_cloud_fn_,
PointCloudProcessor_PostProcessingFn post_processing_fn_)
: frame(frame_id),
pixel_shift_by_row(info.format.pixel_shift_by_row),
cloud{info.format.columns_per_frame,
info.format.pixels_per_column / rows_step},
min_range_(min_range), max_range_(max_range),
pc_msgs(get_n_returns(info)),
scan_to_cloud_fn(scan_to_cloud_fn_),
post_processing_fn(post_processing_fn_) {
for (size_t i = 0; i < pc_msgs.size(); ++i)
pc_msgs[i] = std::make_shared<sensor_msgs::PointCloud2>();
ouster::mat4d additional_transform =
apply_lidar_to_sensor_transform ? info.lidar_to_sensor_transform
: ouster::mat4d::Identity();
auto xyz_lut = ouster::make_xyz_lut(
info.format.columns_per_frame, info.format.pixels_per_column,
ouster::sensor::range_unit, info.beam_to_lidar_transform,
additional_transform, info.beam_azimuth_angles,
info.beam_altitude_angles);
// The ouster_ros drive currently only uses single precision when it
// produces the point cloud. So it isn't of a benefit to compute point
// cloud xyz coordinates using double precision (for the time being).
lut_direction = xyz_lut.direction.cast<float>();
lut_offset = xyz_lut.offset.cast<float>();
points = ouster::PointsF(lut_direction.rows(), lut_offset.cols());
if (!mask_path.empty()) {
cv::Mat image = cv::imread(mask_path,
cv::IMREAD_GRAYSCALE);
if (image.empty()) {
throw std::runtime_error("Failed to load mask image from path: " + mask_path);
}
Eigen::MatrixXi eigen_img(image.rows, image.cols);
cv::cv2eigen(image, eigen_img);
mask = eigen_img.cast<uint32_t>() / 255;
}
}
private:
template <typename T>
void pcl_toROSMsg(const ouster_ros::Cloud<T>& pcl_cloud,
sensor_msgs::PointCloud2& cloud) {
// TODO: remove the staging step in the future
pcl::toPCLPointCloud2(pcl_cloud, staging_pcl_pc2);
pcl_conversions::moveFromPCL(staging_pcl_pc2, cloud);
}
void process(const ouster::LidarScan& lidar_scan, uint64_t scan_ts,
const ros::Time& msg_ts) {
for (int i = 0; i < static_cast<int>(pc_msgs.size()); ++i) {
auto range_ch = static_cast<sensor::ChanField>(sensor::ChanField::RANGE + i);
auto range = lidar_scan.field<uint32_t>(range_ch);
auto range_masked = mask.rows() == range.rows() && mask.cols() == range.cols() ? range * mask : range;
ouster::cartesianT(points, range_masked, lut_direction, lut_offset,
min_range_, max_range_,
std::numeric_limits<float>::quiet_NaN());
scan_to_cloud_fn(cloud, points, scan_ts, lidar_scan,
pixel_shift_by_row, i);
pcl_toROSMsg(cloud, *pc_msgs[i]);
pc_msgs[i]->header.stamp = msg_ts;
pc_msgs[i]->header.frame_id = frame;
}
if (post_processing_fn) post_processing_fn(pc_msgs);
}
public:
static LidarScanProcessor create(const ouster::sensor::sensor_info& info,
const std::string& frame,
bool apply_lidar_to_sensor_transform,
uint32_t min_range, uint32_t max_range,
int rows_step, const std::string& mask_path,
ScanToCloudFn scan_to_cloud_fn_,
PointCloudProcessor_PostProcessingFn post_processing_fn) {
auto handler = std::make_shared<PointCloudProcessor>(
info, frame, apply_lidar_to_sensor_transform,
min_range, max_range, rows_step, mask_path,
scan_to_cloud_fn_, post_processing_fn);
return [handler](const ouster::LidarScan& lidar_scan, uint64_t scan_ts,
const ros::Time& msg_ts) {
handler->process(lidar_scan, scan_ts, msg_ts);
};
}
private:
// a buffer used for staging during the conversion
// from a PCL point cloud to a ros point cloud message
pcl::PCLPointCloud2 staging_pcl_pc2;
std::string frame;
ouster::PointsF lut_direction;
ouster::PointsF lut_offset;
ouster::PointsF points;
std::vector<int> pixel_shift_by_row;
ouster_ros::Cloud<PointT> cloud;
uint32_t min_range_;
uint32_t max_range_;
PointCloudProcessor_OutputType pc_msgs;
ScanToCloudFn scan_to_cloud_fn;
PointCloudProcessor_PostProcessingFn post_processing_fn;
ouster::img_t<uint32_t> mask;
};
} // namespace ouster_ros