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


multisense_ros
Author(s):
autogenerated on Thu Feb 20 2025 03:51:03