ground_surface_utilities.cpp
Go to the documentation of this file.
1 
35 
36 namespace ground_surface_utilities {
37 
38 namespace {
39 
40 float computeAzimuth(const float a, const float b)
41 {
42  return a >= 0 ? static_cast<float>(M_PI) - atan(b / (a + std::numeric_limits<float>::epsilon()))
43  : atan(b / -(a + std::numeric_limits<float>::epsilon()));
44 }
45 
46 float computeRange(const float a, const float b)
47 {
48  return sqrt(pow(a, 2) + pow(b, 2));
49 }
50 
51 template <typename T> T computeQuadraticSurface(T x, T z, const T* params)
52 {
57  return params[0] * pow(x, 2) + params[1] * pow(z, 2) + params[2] * x * z +
58  params[3] * x + params[4] * z + params[5];
59 }
60 
61 Eigen::Matrix<Eigen::Matrix<float, Eigen::Dynamic, 1>, Eigen::Dynamic, 1> generateBasisArray()
62 {
63  Eigen::Matrix<Eigen::Matrix<float, Eigen::Dynamic, 1>, Eigen::Dynamic, 1> ret(4);
64 
65  // Temporary storage for polynomial coefficients
66  Eigen::Matrix<float, Eigen::Dynamic, 1> basisCoefficients(4);
67 
68  // The following basis functions are copied from Lee, Wolberg, and Shin,
69  // "Scattered Data Interpolation with Multilevel B-Splines",
70  // IEEE Transactions on Visualization and Computer Graphics, Vol 3, 228-244, 1997.
71 
72  // First cubic spline basis component is
73  // B(s) = (1 - s)**3 / 6.
74  // This expands to
75  // B(s) = -(1/6)s**3 + (1/2)s**2 - (1/2)s + 1/6.
76  basisCoefficients(0) = 1.0 / 6.0;
77  basisCoefficients(1) = -0.5;
78  basisCoefficients(2) = 0.5;
79  basisCoefficients(3) = -1.0 / 6.0;
80  ret(0) = basisCoefficients;
81 
82  // Second cubic spline basis component is
83  // B(s) = (1/2)t**3 - t**2 + 2/3.
84  basisCoefficients(0) = 2.0 / 3.0;
85  basisCoefficients(1) = 0.0;
86  basisCoefficients(2) = -1.0;
87  basisCoefficients(3) = 0.5;
88  ret(1) = basisCoefficients;
89 
90  // Third cubic spline basis component is
91  // B(s) = -(1/2)t**3 + (1/2)t**2 + (1/2)t + 1/6.
92  basisCoefficients(0) = 1.0 / 6.0;
93  basisCoefficients(1) = 0.5;
94  basisCoefficients(2) = 0.5;
95  basisCoefficients(3) = -0.5;
96  ret(2) = basisCoefficients;
97 
98  // Fourth cubic spline basis component is
99  // B(s) = (1/6)t**3.
100  basisCoefficients(0) = 0.0;
101  basisCoefficients(1) = 0.0;
102  basisCoefficients(2) = 0.0;
103  basisCoefficients(3) = 1.0 / 6.0;
104  ret(3) = basisCoefficients;
105 
106  return ret;
107 }
108 
109 template <class FloatT>
110 FloatT getSplineValue(
111  const FloatT* xzCellOrigin,
112  const FloatT* xzCellSize,
113  const FloatT sValue,
114  const FloatT tValue,
115  const Eigen::Matrix<FloatT, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &controlGrid,
116  const Eigen::Matrix<Eigen::Matrix<FloatT, Eigen::Dynamic, 1>, Eigen::Dynamic, 1> &basisArray)
117 {
118  // Find the integer coords of the control grid cell in which the input point lies.
119  FloatT iTmp = (sValue - xzCellOrigin[0]) / xzCellSize[0];
120  FloatT jTmp = (tValue - xzCellOrigin[1]) / xzCellSize[1];
121 
122  int iCoord = static_cast<int>(floor(iTmp));
123  int jCoord = static_cast<int>(floor(jTmp));
124 
125  // Find real valued coords within the cell, along with all of the powers of those coords
126  // we'll be wanting to plug into spline basis functions.
127  FloatT powersOfS[4];
128  powersOfS[0] = FloatT(1.0);
129  powersOfS[1] = iTmp - FloatT(iCoord);
130  powersOfS[2] = powersOfS[1] * powersOfS[1];
131  powersOfS[3] = powersOfS[2] * powersOfS[1];
132  FloatT powersOfT[4];
133  powersOfT[0] = FloatT(1.0);
134  powersOfT[1] = jTmp - FloatT(jCoord);
135  powersOfT[2] = powersOfT[1] * powersOfT[1];
136  powersOfT[3] = powersOfT[2] * powersOfT[1];
137 
138  size_t iIndex = static_cast<size_t>(iCoord);
139  size_t jIndex = static_cast<size_t>(jCoord);
140 
141  // Interpolate by adding spline basis functions from the surrounding control points.
142  int index0 = iIndex - 1;
143  int index1 = jIndex - 1;
144  FloatT functionValue = 0.0;
145  for (size_t kIndex = 0; kIndex < 4; ++kIndex)
146  {
147  size_t i0PlusK = index0 + kIndex;
148 
149  FloatT B_k = std::inner_product(
150  powersOfS, powersOfS + 4, (basisArray)[kIndex].data(), static_cast<FloatT>(0));
151 
152  for (size_t lIndex = 0; lIndex < 4; ++lIndex)
153  {
154  size_t i1PlusL = index1 + lIndex;
155 
156  FloatT B_l = std::inner_product(
157  powersOfT, powersOfT + 4, (basisArray)[lIndex].data(), static_cast<FloatT>(0));
158 
159  functionValue += (B_k * B_l * controlGrid(i1PlusL, i0PlusK));
160  }
161  }
162  return functionValue;
163 }
164 
165 } // anonymous namespace
166 
167 Eigen::Matrix<uint8_t, 3, 1> groundSurfaceClassToPixelColor(const uint8_t value)
168 {
169  if (value == 1) // Out of bounds
170  return Eigen::Matrix<uint8_t, 3, 1>{ 114, 159, 207 };
171  else if (value == 2) // Obstacle
172  return Eigen::Matrix<uint8_t, 3, 1>{ 255, 0, 0 };
173  else if (value == 3) // Free space
174  return Eigen::Matrix<uint8_t, 3, 1>{ 0, 255, 0 };
175 
176  // Unknown
177  return Eigen::Matrix<uint8_t, 3, 1>{ 0, 0, 0 };
178 }
179 
180 sensor_msgs::PointCloud2 eigenToPointcloud(
181  const std::vector<Eigen::Vector3f, Eigen::aligned_allocator<Eigen::Vector3f>> &input,
182  const std::string &frame_id)
183 {
184  sensor_msgs::PointCloud2 ret =
185  multisense_ros::initialize_pointcloud<float>(true, frame_id, "intensity");
186 
187  const double num_points = input.size();
188  ret.data.resize(num_points * ret.point_step);
189 
190  for (size_t i = 0; i < num_points; ++i)
191  {
192  float* cloudP = reinterpret_cast<float*>(&(ret.data[i * ret.point_step]));
193  cloudP[0] = input[i][0];
194  cloudP[1] = input[i][1];
195  cloudP[2] = input[i][2];
196  }
197 
198  ret.height = 1;
199  ret.row_step = num_points * ret.point_step;
200  ret.width = num_points;
201  ret.data.resize(num_points * ret.point_step);
202 
203  return ret;
204 }
205 
206 std::vector<Eigen::Vector3f, Eigen::aligned_allocator<Eigen::Vector3f>> convertSplineToPointcloud(
207  const Eigen::Matrix<float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> &controlGrid,
208  const SplineDrawParameters &splineDrawParams,
209  const double pointcloudMaxRange,
210  const float* xzCellOrigin,
211  const float* xzCellSize,
212  const float* minMaxAzimuthAngle,
213  const float* extrinsics,
214  const float* quadraticParams,
215  const float baseline)
216 {
217  static const auto basisArray = generateBasisArray();
218 
219  // Generate extrinsics matrix
220  Eigen::Matrix<float, 4, 4> extrinsicsMat;
221  {
222  extrinsicsMat.setZero();
223  extrinsicsMat(0, 3) = extrinsics[0];
224  extrinsicsMat(1, 3) = extrinsics[1];
225  extrinsicsMat(2, 3) = extrinsics[2];
226  extrinsicsMat(3, 3) = static_cast<float>(1.0);
227  Eigen::Matrix<float, 3, 3> rot =
228  (Eigen::AngleAxis<float>(extrinsics[5], Eigen::Matrix<float, 3, 1>(0, 0, 1))
229  * Eigen::AngleAxis<float>(extrinsics[4], Eigen::Matrix<float, 3, 1>(0, 1, 0))
230  * Eigen::AngleAxis<float>(extrinsics[3], Eigen::Matrix<float, 3, 1>(1, 0, 0))).matrix();
231  extrinsicsMat.block(0, 0, 3, 3) = rot;
232  }
233 
234  // Precompute extrinsics inverse
235  const auto extrinsicsInverse = extrinsicsMat.inverse();
236 
237  // Precompute number of points that will be drawn
238  const size_t numPoints =
239  std::floor((splineDrawParams.max_x_m - splineDrawParams.min_x_m) / splineDrawParams.resolution) *
240  std::floor((splineDrawParams.max_z_m - splineDrawParams.min_z_m) / splineDrawParams.resolution);
241 
242  std::vector<Eigen::Vector3f, Eigen::aligned_allocator<Eigen::Vector3f>> points;
243  points.reserve(numPoints);
244 
245  for (float x = splineDrawParams.min_x_m; x < splineDrawParams.max_x_m; x += splineDrawParams.resolution)
246  {
247  for (float z = splineDrawParams.min_z_m; z < splineDrawParams.max_z_m; z += splineDrawParams.resolution)
248  {
249  // Compute spline point and transform into left camera optical frame
250  const auto y = getSplineValue(xzCellOrigin, xzCellSize, x, z, controlGrid, basisArray)
251  + computeQuadraticSurface(x, z, quadraticParams);
252 
253  const Eigen::Vector3f splinePoint = Eigen::Vector3f(x, y, z);
254  const Eigen::Vector3f transformedSplinePoint = (extrinsicsInverse * splinePoint.homogeneous()).hnormalized();
255 
256  // Filter points by range and angle
257  const auto distance = computeRange(transformedSplinePoint(0), transformedSplinePoint(2));
258  if (distance > pointcloudMaxRange)
259  continue;
260 
261  const auto leftCamAzimuthAngle = computeAzimuth(transformedSplinePoint(0), transformedSplinePoint(2));
262  if (leftCamAzimuthAngle < minMaxAzimuthAngle[0])
263  continue;
264 
265  // Offset max azimuth angle check by baseline for a cleaner "frustum" visualization
266  const auto rightCamAzimuthAngle = computeAzimuth(transformedSplinePoint(0) + baseline, transformedSplinePoint(2));
267  if (rightCamAzimuthAngle > minMaxAzimuthAngle[1])
268  continue;
269 
270  points.emplace_back(splinePoint);
271  }
272  }
273 
274  points.shrink_to_fit();
275 
276  return points;
277 }
278 
279 } // namespace ground_surface_utilities
data
double max_z_m
The max range (along the z dimension / optical axis) to draw the B-spline model.
INLINE Rall1d< T, V, S > atan(const Rall1d< T, V, S > &x)
double max_x_m
The max width (along the x dimension) to draw the B-spline model.
Struct containing parameters for drawing a pointcloud representation of a B-Spline model...
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
sensor_msgs::PointCloud2 eigenToPointcloud(const std::vector< Eigen::Vector3f, Eigen::aligned_allocator< Eigen::Vector3f >> &input, const std::string &frame_id)
Convert an eigen representation of a pointcloud to a ROS sensor_msgs::PointCloud2 format...
Eigen::Matrix< uint8_t, 3, 1 > groundSurfaceClassToPixelColor(const uint8_t value)
Look up table to convert a ground surface class value into into a color for visualization (out-of-bou...
double min_x_m
The min width (along the x dimension) to draw the B-spline model.
double resolution
The resolution to sample the B-Spline model for drawing.
double min_z_m
The min range (along the z dimension / optical axis) to draw the B-spline model.
std::vector< Eigen::Vector3f, Eigen::aligned_allocator< Eigen::Vector3f > > convertSplineToPointcloud(const Eigen::Matrix< float, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > &controlGrid, const SplineDrawParameters &splineDrawParams, const double pointcloudMaxRange, const float *xzCellOrigin, const float *xzCellSize, const float *minMaxAzimuthAngle, const float *extrinsics, const float *quadraticParams, const float baseline)
Generate a pointcloud representation of a b-spline ground surface model for visualization.


multisense_ros
Author(s):
autogenerated on Sat Jun 24 2023 03:01:30