SignedDistance2d.cpp
Go to the documentation of this file.
1 /*
2 * SignedDistance2d.cpp
3 *
4 * Created on: Jul 10, 2020
5 * Author: Ruben Grandia
6 * Institute: ETH Zurich
7 */
8 
10 
12 
13 namespace grid_map {
14 namespace signed_distance_field {
15 
16 namespace internal {
18  float v; // origin of bounding function
19  float f; // functional offset at the origin
20  float z_lhs; // lhs of interval where this bound holds
21  float z_rhs; // rhs of interval where this lower bound holds
22 };
23 
30 std::vector<DistanceLowerBound>::iterator fillLowerBounds(const Eigen::Ref<Eigen::VectorXf>& squareDistance1d,
31  std::vector<DistanceLowerBound>& lowerBounds, Eigen::Index start) {
32  const auto n = squareDistance1d.size();
33  const auto nFloat = static_cast<float>(n);
34  const auto startFloat = static_cast<float>(start);
35 
36  // Initialize
37  auto rhsBoundIt = lowerBounds.begin();
38  *rhsBoundIt = DistanceLowerBound{startFloat, squareDistance1d[start], -INF, INF};
39  auto firstBoundIt = lowerBounds.begin();
40 
41  // Compute bounds to the right of minimum
42  float qFloat = rhsBoundIt->v + 1.0F;
43  for (Eigen::Index q = start + 1; q < n; ++q) {
44  // Storing this by value gives better performance (removed indirection?)
45  const float fq = squareDistance1d[q];
46 
47  float s = equidistancePoint(qFloat, fq, rhsBoundIt->v, rhsBoundIt->f);
48  if (s < nFloat) { // Can ignore the lower bound derived from this point if it is only active outsize of [start, n]
49  // Search backwards in bounds until s is within [z_lhs, z_rhs]
50  while (s < rhsBoundIt->z_lhs) {
51  --rhsBoundIt;
52  s = equidistancePoint(qFloat, fq, rhsBoundIt->v, rhsBoundIt->f);
53  }
54  if (s > startFloat) { // Intersection is within [start, n]. Adjust current lowerbound and insert the new one after
55  rhsBoundIt->z_rhs = s; // Update the bound that comes before
56  ++rhsBoundIt; // insert new bound after.
57  *rhsBoundIt = DistanceLowerBound{qFloat, fq, s, INF}; // Valid from s till infinity
58  } else { // Intersection is outside [0, n]. This means that the new bound dominates all previous bounds
59  *rhsBoundIt = DistanceLowerBound{qFloat, fq, -INF, INF};
60  firstBoundIt = rhsBoundIt; // No need to keep other bounds, so update the first bound iterator to this one.
61  }
62  }
63 
64  // Increment to follow loop counter as float
65  qFloat += 1.0F;
66  }
67 
68  return firstBoundIt;
69 }
70 
71 void extractSquareDistances(Eigen::Ref<Eigen::VectorXf> squareDistance1d, std::vector<DistanceLowerBound>::const_iterator lowerBoundIt,
72  Eigen::Index start) {
73  const auto n = squareDistance1d.size();
74 
75  // Store active bound by value to remove indirection
76  auto lastz = lowerBoundIt->z_rhs;
77 
78  auto qFloat = static_cast<float>(start);
79  for (Eigen::Index q = start; q < n; ++q) {
80  if (squareDistance1d[q] > 0.0F) { // Observe that if squareDistance1d[q] == 0.0, this is already the minimum and it will stay 0.0
81  // Find the new active lower bound if q no longer belongs to current interval
82  if (qFloat > lastz) {
83  do {
84  ++lowerBoundIt;
85  } while (lowerBoundIt->z_rhs < qFloat);
86  lastz = lowerBoundIt->z_rhs;
87  }
88 
89  squareDistance1d[q] = squarePixelBorderDistance(qFloat, lowerBoundIt->v, lowerBoundIt->f);
90  }
91 
92  qFloat += 1.0F;
93  }
94 }
95 
100 void extractDistances(Eigen::Ref<Eigen::VectorXf> squareDistance1d,
101  std::vector<DistanceLowerBound>::const_iterator lowerBoundIt, Eigen::Index start) {
102  const auto n = squareDistance1d.size();
103 
104  // Store active bound by value to remove indirection
105  auto lastz = lowerBoundIt->z_rhs;
106 
107  auto qFloat = static_cast<float>(start);
108  for (Eigen::Index q = start; q < n; ++q) {
109  if (squareDistance1d[q] > 0.0F) { // Observe that if squareDistance1d[q] == 0.0, this is already the minimum and it will stay 0.0
110  // Find the new active lower bound if q no longer belongs to current interval
111  if (qFloat > lastz) {
112  do {
113  ++lowerBoundIt;
114  } while (lowerBoundIt->z_rhs < qFloat);
115  lastz = lowerBoundIt->z_rhs;
116  }
117 
118  squareDistance1d[q] = std::sqrt(squarePixelBorderDistance(qFloat, lowerBoundIt->v, lowerBoundIt->f));
119  }
120 
121  qFloat += 1.0F;
122  }
123 }
124 
128 Eigen::Index lastZeroFromFront(const Eigen::Ref<Eigen::VectorXf>& squareDistance1d) {
129  const auto n = squareDistance1d.size();
130 
131  for (Eigen::Index q = 0; q < n; ++q) {
132  if (squareDistance1d[q] > 0.0F) {
133  if (q > 0) {
134  return q - 1;
135  } else {
136  return 0;
137  }
138  }
139  }
140  return n;
141 }
142 
143 inline void squaredDistanceTransform_1d_inplace(Eigen::Ref<Eigen::VectorXf> squareDistance1d,
144  std::vector<DistanceLowerBound>& lowerBounds) {
145  auto start = lastZeroFromFront(squareDistance1d);
146 
147  // Only need to process line if there are nonzero elements. Also the first zeros stay untouched.
148  if (start < squareDistance1d.size()) {
149  auto startIt = fillLowerBounds(squareDistance1d, lowerBounds, start);
150  extractSquareDistances(squareDistance1d, startIt, start);
151  }
152 }
153 
159 inline void distanceTransform_1d_inplace(Eigen::Ref<Eigen::VectorXf> squareDistance1d, std::vector<DistanceLowerBound>& lowerBounds) {
160  auto start = lastZeroFromFront(squareDistance1d);
161 
162  // Only need to process line if there are nonzero elements. Also the first zeros stay untouched.
163  if (start < squareDistance1d.size()) {
164  auto startIt = fillLowerBounds(squareDistance1d, lowerBounds, start);
165  extractDistances(squareDistance1d, startIt, start);
166  }
167 }
168 
169 void computePixelDistance2dTranspose(Matrix& input, Matrix& distanceTranspose) {
170  const auto n = input.rows();
171  const auto m = input.cols();
172 
173  // Allocate a buffer big enough for processing both rowise and columnwise
174  std::vector<DistanceLowerBound> lowerBounds(std::max(n, m));
175 
176  // Process columns
177  for (Eigen::Index i = 0; i < m; ++i) {
178  squaredDistanceTransform_1d_inplace(input.col(i), lowerBounds);
179  }
180 
181  // Process rows (= columns after transpose).
182  distanceTranspose = input.transpose();
183  for (Eigen::Index i = 0; i < n; ++i) {
184  // Fuses square distance algorithm and taking sqrt.
185  distanceTransform_1d_inplace(distanceTranspose.col(i), lowerBounds);
186  }
187 }
188 
189 // Initialize with square distance in height direction in pixel units if above the surface
190 void initializeObstacleDistance(const Matrix& elevationMap, Matrix& result, float height, float resolution) {
191  /* Vectorized implementation of:
192  * if (height > elevation) {
193  * const auto diff = (height - elevation) / resolution;
194  * return diff * diff;
195  * } else {
196  * return 0.0F;
197  * }
198  */
199  result = ((1.0F / resolution) * (height - elevationMap.array()).cwiseMax(0.0F)).square();
200 }
201 
202 // Initialize with square distance in height direction in pixel units if below the surface
203 void initializeObstacleFreeDistance(const Matrix& elevationMap, Matrix& result, float height, float resolution) {
204  /* Vectorized implementation of:
205  * if (height < elevation) {
206  * const auto diff = (height - elevation) / resolution;
207  * return diff * diff;
208  * } else {
209  * return 0.0F;
210  * }
211  */
212  result = ((1.0F / resolution) * (height - elevationMap.array()).cwiseMin(0.0F)).square();
213 }
214 
215 void pixelDistanceToFreeSpaceTranspose(const Matrix& elevationMap, Matrix& sdfObstacleFree, Matrix& tmp, float height, float resolution) {
216  internal::initializeObstacleFreeDistance(elevationMap, tmp, height, resolution);
217  internal::computePixelDistance2dTranspose(tmp, sdfObstacleFree);
218 }
219 
220 void pixelDistanceToObstacleTranspose(const Matrix& elevationMap, Matrix& sdfObstacleTranspose, Matrix& tmp, float height,
221  float resolution) {
222  internal::initializeObstacleDistance(elevationMap, tmp, height, resolution);
223  internal::computePixelDistance2dTranspose(tmp, sdfObstacleTranspose);
224 }
225 
226 Matrix signedDistanceFromOccupancyTranspose(const Eigen::Matrix<bool, -1, -1>& occupancyGrid, float resolution) {
227  // Compute pixel distance to obstacles
228  Matrix sdfObstacle;
229  Matrix init = occupancyGrid.unaryExpr([=](bool val) { return (val) ? 0.0F : INF; });
231 
232  // Compute pixel distance to obstacle free space
233  Matrix sdfObstacleFree;
234  init = occupancyGrid.unaryExpr([=](bool val) { return (val) ? INF : 0.0F; });
235  internal::computePixelDistance2dTranspose(init, sdfObstacleFree);
236 
237  return resolution * (sdfObstacle - sdfObstacleFree);
238 }
239 
240 } // namespace internal
241 
242 void signedDistanceAtHeightTranspose(const Matrix& elevationMap, Matrix& sdfTranspose, Matrix& tmp, Matrix& tmpTranspose, float height,
243  float resolution, float minHeight, float maxHeight) {
244  const bool allPixelsAreObstacles = height < minHeight;
245  const bool allPixelsAreFreeSpace = height > maxHeight;
246 
247  if (allPixelsAreObstacles) {
248  internal::pixelDistanceToFreeSpaceTranspose(elevationMap, sdfTranspose, tmp, height, resolution);
249 
250  sdfTranspose *= -resolution;
251  } else if (allPixelsAreFreeSpace) {
252  internal::pixelDistanceToObstacleTranspose(elevationMap, sdfTranspose, tmp, height, resolution);
253 
254  sdfTranspose *= resolution;
255  } else { // This layer contains a mix of obstacles and free space
256  internal::pixelDistanceToObstacleTranspose(elevationMap, sdfTranspose, tmp, height, resolution);
257  internal::pixelDistanceToFreeSpaceTranspose(elevationMap, tmpTranspose, tmp, height, resolution);
258 
259  sdfTranspose = resolution * (sdfTranspose - tmpTranspose);
260  }
261 }
262 
263 Matrix signedDistanceAtHeight(const Matrix& elevationMap, float height, float resolution, float minHeight, float maxHeight) {
264  Matrix sdfTranspose;
265  Matrix tmp;
266  Matrix tmpTranspose;
267 
268  signedDistanceAtHeightTranspose(elevationMap, sdfTranspose, tmp, tmpTranspose, height, resolution, minHeight, maxHeight);
269  return sdfTranspose.transpose();
270 }
271 
272 Matrix signedDistanceFromOccupancy(const Eigen::Matrix<bool, -1, -1>& occupancyGrid, float resolution) {
273  auto obstacleCount = occupancyGrid.count();
274  bool hasObstacles = obstacleCount > 0;
275  if (hasObstacles) {
276  bool hasFreeSpace = obstacleCount < occupancyGrid.size();
277  if (hasFreeSpace) {
278  return internal::signedDistanceFromOccupancyTranspose(occupancyGrid, resolution).transpose();
279  } else {
280  // Only obstacles -> distance is minus infinity everywhere
281  return Matrix::Constant(occupancyGrid.rows(), occupancyGrid.cols(), -INF);
282  }
283  } else {
284  // No obstacles -> planar distance is infinite
285  return Matrix::Constant(occupancyGrid.rows(), occupancyGrid.cols(), INF);
286  }
287 }
288 
289 } // namespace signed_distance_field
290 } // namespace grid_map
Matrix signedDistanceFromOccupancyTranspose(const Eigen::Matrix< bool, -1, -1 > &occupancyGrid, float resolution)
float squarePixelBorderDistance(float i, float j, float f)
Eigen::Index lastZeroFromFront(const Eigen::Ref< Eigen::VectorXf > &squareDistance1d)
void distanceTransform_1d_inplace(Eigen::Ref< Eigen::VectorXf > squareDistance1d, std::vector< DistanceLowerBound > &lowerBounds)
void signedDistanceAtHeightTranspose(const Matrix &elevationMap, Matrix &sdfTranspose, Matrix &tmp, Matrix &tmpTranspose, float height, float resolution, float minHeight, float maxHeight)
void extractSquareDistances(Eigen::Ref< Eigen::VectorXf > squareDistance1d, std::vector< DistanceLowerBound >::const_iterator lowerBoundIt, Eigen::Index start)
Matrix signedDistanceAtHeight(const Matrix &elevationMap, float height, float resolution, float minHeight, float maxHeight)
void squaredDistanceTransform_1d_inplace(Eigen::Ref< Eigen::VectorXf > squareDistance1d, std::vector< DistanceLowerBound > &lowerBounds)
void initializeObstacleDistance(const Matrix &elevationMap, Matrix &result, float height, float resolution)
Matrix signedDistanceFromOccupancy(const Eigen::Matrix< bool, -1, -1 > &occupancyGrid, float resolution)
float equidistancePoint(float q, float fq, float p, float fp)
Eigen::MatrixXf Matrix
void initializeObstacleFreeDistance(const Matrix &elevationMap, Matrix &result, float height, float resolution)
void extractDistances(Eigen::Ref< Eigen::VectorXf > squareDistance1d, std::vector< DistanceLowerBound >::const_iterator lowerBoundIt, Eigen::Index start)
void pixelDistanceToFreeSpaceTranspose(const Matrix &elevationMap, Matrix &sdfObstacleFree, Matrix &tmp, float height, float resolution)
void pixelDistanceToObstacleTranspose(const Matrix &elevationMap, Matrix &sdfObstacleTranspose, Matrix &tmp, float height, float resolution)
std::vector< DistanceLowerBound >::iterator fillLowerBounds(const Eigen::Ref< Eigen::VectorXf > &squareDistance1d, std::vector< DistanceLowerBound > &lowerBounds, Eigen::Index start)
constexpr float INF
Distance value that is considered infinite.
Definition: Utils.hpp:18
void computePixelDistance2dTranspose(Matrix &input, Matrix &distanceTranspose)


grid_map_sdf
Author(s): Takahiro Miki , Péter Fankhauser
autogenerated on Wed Jul 5 2023 02:23:42