-
Notifications
You must be signed in to change notification settings - Fork 4
/
organize_velodyne_cloud.h
133 lines (117 loc) · 4.69 KB
/
organize_velodyne_cloud.h
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
/* Usage:
*
* OrganizePointCloud<pcl::PointXYZ>(ros_msg, pcl_cloud, 32);
*
* Note: Explicitly declaring the type (in the above example: pcl::PointXYZ) is
* required for the templating to work properly.
*/
#ifndef ORGANIZE_VELODYNE_CLOUD_H
#define ORGANIZE_VELODYNE_CLOUD_H
// Eigen includes
#include <Eigen/StdVector>
#include <eigen3/Eigen/Geometry> // Required for matrix math
// PCL includes
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>
// ROS includes
#include <sensor_msgs/PointCloud2.h>
// Misc includes
#include <velodyne_pointcloud/point_types.h>
using namespace pcl;
using namespace std;
/** @brief Takes in a partially organized cloud and fills the gaps in the
* current column with dummy points whose coordinates are NaN
* @param cloud - A partially organized point cloud
* @param row_count - A vector which maintains the number of points in each
* row. It's length is equal to the number of rows
* @param col - The current column number, i.e. the number of points
* that should be in each row. Each element in row_count
* should be equal to this number
*/
template <typename PointType> void
FillWithNaNs(PointCloud<PointType>& cloud, vector<int>& row_count, int col)
{
for (int k=0; k < row_count.size(); k++)
{
if (row_count[k] <= col)
{
PointType p_nan;
p_nan.x = numeric_limits<float>::quiet_NaN();
p_nan.y = numeric_limits<float>::quiet_NaN();
p_nan.z = numeric_limits<float>::quiet_NaN();
cloud(col, k) = p_nan;
row_count[k]++;
}
}
}
/** @brief Takes in a ROS PointCloud2 object and outputs a PCL templated
* point cloud type, that has been organized. The input MUST be a
* cloud from a Velodyne LiDAR unit
* @param msg - The input ROS point cloud message from a Velodyne LiDAR
* @param cloud - The organized cloud output
* @param rings - The number of rings in the Velodyne LiDAR unit
*/
template <typename PointType> void
OrganizePointCloud(sensor_msgs::PointCloud2 msg,
PointCloud<PointType>& cloud,
int rings)
{
// Convert the PC to the PointXYZIR point type so we can access the ring
PointCloud<velodyne_pointcloud::PointXYZIR>::Ptr cloud_XYZIR
(new PointCloud<velodyne_pointcloud::PointXYZIR>);
pcl::PCLPointCloud2 pcl_pc2;
pcl_conversions::toPCL(msg, pcl_pc2);
fromPCLPointCloud2(pcl_pc2, *cloud_XYZIR);
/* Because not every row is complete, we can't know in advance how big the
organized cloud needs to be, so we deliberately over-size by 30%. We then
create a temporary cloud, SPECIFICALLY with the organized cloud
constructor*/
int columns = (cloud_XYZIR->points.size() / rings) * 2.0;
PointCloud<pcl::PointXYZRGBNormal>::Ptr tmp_cloud_ptr (new PointCloud<pcl::PointXYZRGBNormal>);
tmp_cloud_ptr->points.resize(columns * rings);
tmp_cloud_ptr->height = static_cast<uint32_t>(rings);
tmp_cloud_ptr->width = static_cast<uint32_t>(columns);
tmp_cloud_ptr->is_dense = false;
/* Iterate through the XYZIR points and fill the TMP cloud where the
ring number determines the row the point is inserted into */
int ring;
int col = 0;
vector<int> row_count(rings, 0);
for (int i=0; i < cloud_XYZIR->points.size(); i++)
{
ring = cloud_XYZIR->points[i].ring;
/* If true, we're just about to start processing the next 'column' of data
and need to quickly fill all the holes in the current column with NaNs
to act as dummy points - important for preserving the structure of the
organized cloud */
if (row_count[ring] > col)
{
FillWithNaNs(*tmp_cloud_ptr, row_count, col);
col++;
}
PointType p;
p.x = cloud_XYZIR->points[i].x;
p.y = cloud_XYZIR->points[i].y;
p.z = cloud_XYZIR->points[i].z;
tmp_cloud_ptr->at(row_count[ring], ring) = p; // cloud(col, row)
row_count[ring]++;
}
FillWithNaNs(*tmp_cloud_ptr, row_count, col); // Fill that last column
/* Now we copy the organized tmp cloud to our output cloud, which we can now
size correctly, knowing EXACTLY how many points are in each ring/row. But
we have to do this point-by-point (rather than using memcpy) because we
the points in memory that we want to keep are interpersed with points
we want to leave behind */
cloud = PointCloud<PointType>(row_count[0], rings);
cloud.height = static_cast<uint32_t>(rings);
cloud.width = static_cast<uint32_t>(row_count[0]);
cloud.is_dense = false;
for (int col=0; col < row_count[0]; col++)
{
for (int row=0; row < rings; row++)
{
cloud(col, row) = tmp_cloud_ptr->at(col, row);
}
}
}
#endif // ORGANIZE_VELODYNE_CLOUD_H