CubicInterpolation.cpp
Go to the documentation of this file.
1 /*
2  * CubicInterpolation.cpp
3  *
4  * Created on: Jan 21, 2020
5  * Author: Edo Jelavic
6  * Institute: ETH Zurich, Robotic Systems Lab
7  */
8 
10 
12 
13 namespace grid_map {
14 
15 unsigned int bindIndexToRange(unsigned int idReq, unsigned int nElem)
16 {
17  if (idReq >= nElem) {
18  return (nElem - 1);
19  }
20  return idReq;
21 }
22 
23 double getLayerValue(const Matrix &layerMat, unsigned int rowReq, unsigned int colReq)
24 {
25  const auto numCol = layerMat.cols();
26  const auto numRow = layerMat.rows();
27  const unsigned int iBoundToRange = bindIndexToRange(rowReq, numRow);
28  const unsigned int jBoundToRange = bindIndexToRange(colReq, numCol);
29  return layerMat(iBoundToRange, jBoundToRange);
30 }
31 
32 
42 namespace bicubic_conv {
43 
44 bool evaluateBicubicConvolutionInterpolation(const GridMap &gridMap, const std::string &layer,
45  const Position &queriedPosition,
46  double *interpolatedValue)
47 {
48  FunctionValueMatrix functionValues;
49  if (!assembleFunctionValueMatrix(gridMap, layer, queriedPosition, &functionValues)) {
50  return false;
51  }
52 
53  Position normalizedCoordinate;
54  if (!getNormalizedCoordinates(gridMap, queriedPosition, &normalizedCoordinate)) {
55  return false;
56  }
57 
58  const double tx = normalizedCoordinate.x();
59  const double ty = normalizedCoordinate.y();
60 
61  //bm1 stands for b minus one, i.e. index decreased by one
62  //b2 stands for b plus 2, i.e. index increased by two
63  const double bm1 = convolve1D(tx, functionValues.row(0));
64  const double b0 = convolve1D(tx, functionValues.row(1));
65  const double b1 = convolve1D(tx, functionValues.row(2));
66  const double b2 = convolve1D(tx, functionValues.row(3));
67  const Eigen::Vector4d vectorBs(bm1, b0, b1, b2);
68  *interpolatedValue = convolve1D(ty, vectorBs);
69  return true;
70 }
71 
72 double convolve1D(double t, const Eigen::Vector4d &functionValues)
73 {
74  const Eigen::Vector4d tVector(1.0, t, t * t, t * t * t);
75  const Eigen::Vector4d temp = cubicInterpolationConvolutionMatrix
76  * functionValues;
77  const double retVal = 0.5 * tVector.transpose() * temp;
78  return retVal;
79 }
80 
81 bool assembleFunctionValueMatrix(const GridMap &gridMap, const std::string &layer,
82  const Position &queriedPosition, FunctionValueMatrix *data)
83 {
84 
85  Index middleKnotIndex;
86  if (!getIndicesOfMiddleKnot(gridMap, queriedPosition, &middleKnotIndex)) {
87  return false;
88  }
89 
90  const Matrix &layerMatrix = gridMap.get(layer);
91  auto f = [&layerMatrix](unsigned int rowReq, unsigned int colReq) {
92  double retVal = getLayerValue(layerMatrix, rowReq, colReq);
93  return retVal;
94  };
95 
96  const unsigned int i = middleKnotIndex.x();
97  const unsigned int j = middleKnotIndex.y();
98  /*
99  * Notation taken from: https://en.wikipedia.org/wiki/Bicubic_interpolation
100  * increasing f's indices is flipped w.r.t. to the above since in the article
101  * they use a coordinate frame centered around (i,j). Therefore:
102  * f(i+1,j-1) in their notation corresponds to f(i-1,j+1) in ours. This is
103  * because our coordinate frame sits in the top left corner, see
104  * https://github.com/ANYbotics/grid_map
105  */
106  *data << f(i + 1, j + 1), f(i, j + 1), f(i - 1, j + 1), f(i - 2, j + 1), f(i + 1, j), f(i, j), f(
107  i - 1, j), f(i - 2, j), f(i + 1, j - 1), f(i, j - 1), f(i - 1, j - 1), f(i - 2, j - 1), f(
108  i + 1, j - 2), f(i, j - 2), f(i - 1, j - 2), f(i - 2, j - 2);
109 
110  return true;
111 }
112 
113 bool getNormalizedCoordinates(const GridMap &gridMap, const Position &queriedPosition,
114  Position *position)
115 {
116  Index index;
117  if (!getIndicesOfMiddleKnot(gridMap, queriedPosition, &index)) {
118  return false;
119  }
120 
121  Position middleKnot;
122  if (!gridMap.getPosition(index, middleKnot)) {
123  return false;
124  }
125 
126  position->x() = (queriedPosition.x() - middleKnot.x()) / gridMap.getResolution();
127  position->y() = (queriedPosition.y() - middleKnot.y()) / gridMap.getResolution();
128 
129  return true;
130 }
131 
132 bool getIndicesOfMiddleKnot(const GridMap &gridMap, const Position &queriedPosition, Index *index)
133 {
134  return gridMap.getIndex(queriedPosition, *index);
135 }
136 
137 } /* namespace bicubic_conv */
138 
148 namespace bicubic {
149 
150 bool evaluateBicubicInterpolation(const GridMap &gridMap, const std::string &layer,
151  const Position &queriedPosition, double *interpolatedValue)
152 {
153 
154  const Matrix& layerMat = gridMap.get(layer);
155  const double resolution = gridMap.getResolution();
156 
157  // get indices of data points needed for interpolation
158  IndicesMatrix unitSquareCornerIndices;
159  if (!getUnitSquareCornerIndices(gridMap, queriedPosition, &unitSquareCornerIndices)) {
160  return false;
161  }
162 
163  // get function values
164  DataMatrix f;
165  if (!getFunctionValues(layerMat, unitSquareCornerIndices, &f)) {
166  return false;
167  }
168 
169  // get the first derivatives in x
170  DataMatrix dfx;
171  if (!getFirstOrderDerivatives(layerMat, unitSquareCornerIndices, Dim2D::X, resolution, &dfx)) {
172  return false;
173  }
174 
175  // the first derivatives in y
176  DataMatrix dfy;
177  if (!getFirstOrderDerivatives(layerMat, unitSquareCornerIndices, Dim2D::Y, resolution, &dfy)) {
178  return false;
179  }
180  // mixed derivatives in x y
181  DataMatrix ddfxy;
182  if (!getMixedSecondOrderDerivatives(layerMat, unitSquareCornerIndices, resolution, &ddfxy)) {
183  return false;
184  }
185 
186  // assemble function value matrix matrix
187  FunctionValueMatrix functionValues;
188  assembleFunctionValueMatrix(f, dfx, dfy, ddfxy, &functionValues);
189 
190  // get normalized coordinates
191  Position normalizedCoordinates;
192  if (!computeNormalizedCoordinates(gridMap, unitSquareCornerIndices.bottomLeft_, queriedPosition,
193  &normalizedCoordinates)) {
194  return false;
195  }
196 
197  // evaluate polynomial
198  *interpolatedValue = evaluatePolynomial(functionValues, normalizedCoordinates.x(),
199  normalizedCoordinates.y());
200 
201  return true;
202 }
203 
204 bool getUnitSquareCornerIndices(const GridMap &gridMap, const Position &queriedPosition,
205  IndicesMatrix *indicesMatrix)
206 {
207 
208  Index closestPointId;
209  if (!getClosestPointIndices(gridMap, queriedPosition, &closestPointId)) {
210  return false;
211  }
212 
213  Position closestPoint;
214  if (!gridMap.getPosition(closestPointId, closestPoint)) {
215  return false;
216  }
217 
218  const int idx0 = closestPointId.x();
219  const int idy0 = closestPointId.y();
220  const double x0 = closestPoint.x();
221  const double y0 = closestPoint.y();
222  const double x = queriedPosition.x();
223  const double y = queriedPosition.y();
224 
225  if (x > x0) { //first or fourth quadrant
226  if (y > y0) { //first quadrant
227  indicesMatrix->topLeft_ = Index(idx0, idy0 - 1);
228  indicesMatrix->topRight_ = Index(idx0 - 1, idy0 - 1);
229  indicesMatrix->bottomLeft_ = Index(idx0, idy0);
230  indicesMatrix->bottomRight_ = Index(idx0 - 1, idy0);
231  } else { // fourth quadrant
232  indicesMatrix->topLeft_ = Index(idx0, idy0);
233  indicesMatrix->topRight_ = Index(idx0 - 1, idy0);
234  indicesMatrix->bottomLeft_ = Index(idx0, idy0 + 1);
235  indicesMatrix->bottomRight_ = Index(idx0 - 1, idy0 + 1);
236  }
237  } else { // second or third quadrant
238  if (y > y0) { //second quadrant
239  indicesMatrix->topLeft_ = Index(idx0 + 1, idy0 - 1);
240  indicesMatrix->topRight_ = Index(idx0, idy0 - 1);
241  indicesMatrix->bottomLeft_ = Index(idx0 + 1, idy0);
242  indicesMatrix->bottomRight_ = Index(idx0, idy0);
243  } else { // third quadrant
244  indicesMatrix->topLeft_ = Index(idx0 + 1, idy0);
245  indicesMatrix->topRight_ = Index(idx0, idy0);
246  indicesMatrix->bottomLeft_ = Index(idx0 + 1, idy0 + 1);
247  indicesMatrix->bottomRight_ = Index(idx0, idy0 + 1);
248  }
249  }
250 
251  bindIndicesToRange(gridMap, indicesMatrix);
252 
253  return true;
254 
255 }
256 
257 bool getClosestPointIndices(const GridMap &gridMap, const Position &queriedPosition, Index *index)
258 {
259  return gridMap.getIndex(queriedPosition, *index);
260 }
261 
262 bool computeNormalizedCoordinates(const GridMap &gridMap, const Index &originIndex,
263  const Position &queriedPosition, Position *normalizedCoordinates)
264 {
265 
266  Position origin;
267  if (!gridMap.getPosition(originIndex, origin)) {
268  return false;
269  }
270 
271  normalizedCoordinates->x() = (queriedPosition.x() - origin.x()) / gridMap.getResolution();
272  normalizedCoordinates->y() = (queriedPosition.y() - origin.y()) / gridMap.getResolution();
273 
274  return true;
275 
276 }
277 
278 bool getFunctionValues(const Matrix &layerData, const IndicesMatrix &indices, DataMatrix *data)
279 {
280  data->topLeft_ = layerData(indices.topLeft_.x(), indices.topLeft_.y());
281  data->topRight_ = layerData(indices.topRight_.x(), indices.topRight_.y());
282  data->bottomLeft_ = layerData(indices.bottomLeft_.x(), indices.bottomLeft_.y());
283  data->bottomRight_ = layerData(indices.bottomRight_.x(), indices.bottomRight_.y());
284  return true;
285 }
286 
287 void bindIndicesToRange(const GridMap &gridMap, IndicesMatrix *indices)
288 {
289  const unsigned int numCol = gridMap.getSize().y();
290  const unsigned int numRow = gridMap.getSize().x();
291 
292  //top left
293  {
294  const unsigned int iBoundToRange = bindIndexToRange(indices->topLeft_.x(), numRow);
295  const unsigned int jBoundToRange = bindIndexToRange(indices->topLeft_.y(), numCol);
296  indices->topLeft_ = Index(iBoundToRange, jBoundToRange);
297  }
298 
299  //top right
300  {
301  const unsigned int iBoundToRange = bindIndexToRange(indices->topRight_.x(), numRow);
302  const unsigned int jBoundToRange = bindIndexToRange(indices->topRight_.y(), numCol);
303  indices->topRight_ = Index(iBoundToRange, jBoundToRange);
304  }
305 
306  //bottom left
307  {
308  const unsigned int iBoundToRange = bindIndexToRange(indices->bottomLeft_.x(), numRow);
309  const unsigned int jBoundToRange = bindIndexToRange(indices->bottomLeft_.y(), numCol);
310  indices->bottomLeft_ = Index(iBoundToRange, jBoundToRange);
311  }
312 
313  //bottom right
314  {
315  const unsigned int iBoundToRange = bindIndexToRange(indices->bottomRight_.x(), numRow);
316  const unsigned int jBoundToRange = bindIndexToRange(indices->bottomRight_.y(), numCol);
317  indices->bottomRight_ = Index(iBoundToRange, jBoundToRange);
318  }
319 
320 }
321 
322 bool getFirstOrderDerivatives(const Matrix &layerData, const IndicesMatrix &indices, Dim2D dim,
323  double resolution, DataMatrix *derivatives)
324 {
325  derivatives->topLeft_ = firstOrderDerivativeAt(layerData, indices.topLeft_, dim, resolution);
326  derivatives->topRight_ = firstOrderDerivativeAt(layerData, indices.topRight_, dim, resolution);
327  derivatives->bottomLeft_ = firstOrderDerivativeAt(layerData, indices.bottomLeft_, dim,
328  resolution);
329  derivatives->bottomRight_ = firstOrderDerivativeAt(layerData, indices.bottomRight_, dim,
330  resolution);
331  return true;
332 }
333 
334 double firstOrderDerivativeAt(const Matrix &layerData, const Index &index, Dim2D dim,
335  double resolution)
336 {
337  const auto numCol{static_cast<unsigned int>(layerData.cols())};
338  const auto numRow{static_cast<unsigned int>(layerData.rows())};
339 
340  double left;
341  double right;
342  switch (dim) {
343  case Dim2D::X: {
344  left = layerData(bindIndexToRange(index.x() + 1, numRow), index.y());
345  right = layerData(bindIndexToRange(index.x() - 1, numRow), index.y());
346  break;
347  }
348  case Dim2D::Y: {
349  left = layerData(index.x(), bindIndexToRange(index.y() + 1, numCol));
350  right = layerData(index.x(), bindIndexToRange(index.y() - 1, numCol));
351  break;
352  }
353  default: {
354  throw std::runtime_error("Unknown derivative direction");
355  }
356  }
357 
358  const double perturbation = resolution;
359  // central difference approximation
360  // we need to multiply with resolution since we are
361  // operating in scaled coordinates
362  return (right - left) / (2.0 * perturbation) * resolution;
363 }
364 
365 double mixedSecondOrderDerivativeAt(const Matrix &layerData, const Index &index, double resolution)
366 {
367  /*
368  * no need for dimensions since we have to differentiate w.r.t. x and y
369  * the order doesn't matter. Derivative values are the same.
370  * Taken from https://www.mathematik.uni-dortmund.de/~kuzmin/cfdintro/lecture4.pdf
371  */
372  const auto numCol{static_cast<unsigned int>(layerData.cols())};
373  const auto numRow{static_cast<unsigned int>(layerData.rows())};
374 
375  const double f11 = layerData(bindIndexToRange(index.x() - 1, numRow),
376  bindIndexToRange(index.y() - 1, numCol));
377  const double f1m1 = layerData(bindIndexToRange(index.x() - 1, numRow),
378  bindIndexToRange(index.y() + 1, numCol));
379  const double fm11 = layerData(bindIndexToRange(index.x() + 1, numRow),
380  bindIndexToRange(index.y() - 1, numCol));
381  const double fm1m1 = layerData(bindIndexToRange(index.x() + 1, numRow),
382  bindIndexToRange(index.y() + 1, numCol));
383 
384  const double perturbation = resolution;
385  // central difference approximation
386  // we need to multiply with resolution^2 since we are
387  // operating in scaled coordinates. Second derivative scales
388  // with the square of the resolution
389  return (f11 - f1m1 - fm11 + fm1m1) / (4.0 * perturbation * perturbation) * resolution * resolution;
390 
391 }
392 
393 bool getMixedSecondOrderDerivatives(const Matrix &layerData, const IndicesMatrix &indices,
394  double resolution, DataMatrix *derivatives)
395 {
396  derivatives->topLeft_ = mixedSecondOrderDerivativeAt(layerData, indices.topLeft_, resolution);
397  derivatives->topRight_ = mixedSecondOrderDerivativeAt(layerData, indices.topRight_, resolution);
398  derivatives->bottomLeft_ = mixedSecondOrderDerivativeAt(layerData, indices.bottomLeft_,
399  resolution);
400  derivatives->bottomRight_ = mixedSecondOrderDerivativeAt(layerData, indices.bottomRight_,
401  resolution);
402  return true;
403 }
404 
405 double evaluatePolynomial(const FunctionValueMatrix &functionValues, double tx, double ty)
406 {
407  const Eigen::Vector4d xVector(1, tx, tx * tx, tx * tx * tx);
408  const Eigen::Vector4d yVector(1, ty, ty * ty, ty * ty * ty);
409  const Eigen::Matrix4d tempMat = functionValues
410  * bicubicInterpolationMatrix.transpose();
411  const Eigen::Matrix4d polynomialCoeffMatrix = bicubicInterpolationMatrix * tempMat;
412  const Eigen::Vector4d tempVec = polynomialCoeffMatrix * yVector;
413  return xVector.transpose() * tempVec;
414 }
415 
416 void assembleFunctionValueMatrix(const DataMatrix &f, const DataMatrix &dfx, const DataMatrix &dfy,
417  const DataMatrix &ddfxy, FunctionValueMatrix *functionValues)
418 {
419  auto toEigenMatrix = [](const DataMatrix &d)-> Eigen::Matrix2d {
420  Eigen::Matrix2d e;
421  e(0,0) = d.bottomLeft_;
422  e(1,0) = d.bottomRight_;
423  e(0,1) = d.topLeft_;
424  e(1,1) = d.topRight_;
425  return e;
426  };
427 
428  functionValues->block<2, 2>(0, 0) = toEigenMatrix(f);
429  functionValues->block<2, 2>(2, 2) = toEigenMatrix(ddfxy);
430  functionValues->block<2, 2>(0, 2) = toEigenMatrix(dfy);
431  functionValues->block<2, 2>(2, 0) = toEigenMatrix(dfx);
432 }
433 
434 } /* namespace bicubic*/
435 
436 } /* namespace grid_map */
bool getFirstOrderDerivatives(const Matrix &layerData, const IndicesMatrix &indices, Dim2D dim, double resolution, DataMatrix *derivatives)
Eigen::Matrix4d FunctionValueMatrix
void bindIndicesToRange(const GridMap &gridMap, IndicesMatrix *indices)
unsigned int bindIndexToRange(unsigned int idReq, unsigned int nElem)
bool getIndicesOfMiddleKnot(const GridMap &gridMap, const Position &queriedPosition, Index *index)
bool computeNormalizedCoordinates(const GridMap &gridMap, const Index &originIndex, const Position &queriedPosition, Position *normalizedCoordinates)
const Matrix & get(const std::string &layer) const
Definition: GridMap.cpp:104
static const Eigen::Matrix4d bicubicInterpolationMatrix
const Size & getSize() const
Definition: GridMap.cpp:661
double convolve1D(double t, const Eigen::Vector4d &functionValues)
static const Eigen::Matrix4d cubicInterpolationConvolutionMatrix
double firstOrderDerivativeAt(const Matrix &layerData, const Index &index, Dim2D dim, double resolution)
double getResolution() const
Definition: GridMap.cpp:657
Eigen::Vector2d Position
Definition: TypeDefs.hpp:18
double mixedSecondOrderDerivativeAt(const Matrix &layerData, const Index &index, double resolution)
bool getNormalizedCoordinates(const GridMap &gridMap, const Position &queriedPosition, Position *position)
Eigen::Array2i Index
Definition: TypeDefs.hpp:22
Eigen::MatrixXf Matrix
Definition: TypeDefs.hpp:16
bool getFunctionValues(const Matrix &layerData, const IndicesMatrix &indices, DataMatrix *data)
bool getMixedSecondOrderDerivatives(const Matrix &layerData, const IndicesMatrix &indices, double resolution, DataMatrix *derivatives)
double evaluatePolynomial(const FunctionValueMatrix &functionValues, double tx, double ty)
double getLayerValue(const Matrix &layerMat, int rowReq, int colReq)
bool evaluateBicubicConvolutionInterpolation(const GridMap &gridMap, const std::string &layer, const Position &queriedPosition, double *interpolatedValue)
bool getClosestPointIndices(const GridMap &gridMap, const Position &queriedPosition, Index *index)
bool getUnitSquareCornerIndices(const GridMap &gridMap, const Position &queriedPosition, IndicesMatrix *indicesMatrix)
bool getIndex(const Position &position, Index &index) const
Definition: GridMap.cpp:228
bool getPosition(const Index &index, Position &position) const
Definition: GridMap.cpp:232
bool evaluateBicubicInterpolation(const GridMap &gridMap, const std::string &layer, const Position &queriedPosition, double *interpolatedValue)
bool assembleFunctionValueMatrix(const GridMap &gridMap, const std::string &layer, const Position &queriedPosition, FunctionValueMatrix *data)


grid_map_core
Author(s): Péter Fankhauser
autogenerated on Wed Jul 5 2023 02:23:35