Ocean
NonLinearUniversalOptimizationDense.h
Go to the documentation of this file.
1 /*
2  * Copyright (c) Meta Platforms, Inc. and affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  */
7 
8 #ifndef META_OCEAN_GEOMETRY_NON_LINEAR_UNIVERSAL_OPTIMIZATION_DENSE_H
9 #define META_OCEAN_GEOMETRY_NON_LINEAR_UNIVERSAL_OPTIMIZATION_DENSE_H
10 
13 
15 
16 namespace Ocean
17 {
18 
19 namespace Geometry
20 {
21 
22 /**
23  * This class implements an optimization for universal dense problems with one model (optimization problem).
24  * The implementation allows to optimize arbitrary (universal) problems with arbitrary dimensions.
25  * @tparam tModelSize Size of the model, the number of model parameters
26  * @tparam tResultDimension Number of dimensions that result for each element (measurement) after the model has been applied
27  * @tparam tExternalModelSize Size of the external model, the number of model parameters
28  * @ingroup geometry
29  */
30 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize = tModelSize>
32 {
33  public:
34 
35  /**
36  * Definition of a model.
37  */
39 
40  /**
41  * Definition of an external model.
42  */
44 
45  /**
46  * Definition of a model result.
47  */
49 
50  /**
51  * Definition of a callback function for dense value calculation.
52  * The first parameter provides the external model that is applied to determine the value.<br>
53  * The second parameter provides the index of the measurement elements that is used to determine the value.<br>
54  * The third parameter receives the determined value.
55  */
57 
58  /**
59  * Definition of a callback function for dense error calculation.
60  * The first parameter provides the external model that is applied to determine the error.<br>
61  * The second parameter provides the index of the measurement elements that is used to determine the error.<br>
62  * The third parameter receives the determined error.<br>
63  * The return value provides True if both models provide valid information for the measurement element.
64  */
66 
67  /**
68  * Definition of a dense model transformation function.
69  * The transformation function allows to use an external model function for value and error determination while the internal model is used for the internal optimization.<br>
70  * The first parameter provides the internal model.<br>
71  * The second parameter receives the external model.
72  */
74 
75  /**
76  * Definition of a dense model adjustment function.
77  * The adjustment function allows to modify the internal model (the modification should be tiny e.g., a normalization of a vector which has almost length 1) before the model will be accepted or rejected
78  * The first parameter provides the model to be adjusted
79  */
81 
82  protected:
83 
84  /**
85  * This class implements a dense universal optimization provider for universal models and measurement/data values.
86  */
88  {
89  public:
90 
91  /**
92  * Creates a new universal optimization object.
93  * @param model The model to be optimized
94  * @param numberElements Number of elements (measurements) that are used to determine the optimized model
95  * @param valueCallback Callback function that is used to determine the value for an individual element (measurement) by application of the model
96  * @param errorCallback Callback function that is used to determine the error for an individual element (measurement)
97  * @param modelTransformationCallback Callback function allowing to transform the internal model into an extern model if intended
98  * @param modelAdjustmentCallback Callback function allowing to adjust the internal model before it will be accepted or rejected
99  */
100  inline UniversalOptimizationProvider(Model& model, const size_t numberElements, const ValueCallback& valueCallback, const ErrorCallback& errorCallback, const ModelTransformationCallback& modelTransformationCallback, const ModelAdjustmentCallback& modelAdjustmentCallback);
101 
102  /**
103  * Determines the jacobian matrix for the current camera.
104  * @param jacobian Jacobian matrix
105  */
106  void determineJacobian(Matrix& jacobian) const;
107 
108  /**
109  * Applies the model correction and stores the new model as candidate
110  * @param deltas Optimization deltas that define the correction
111  */
112  inline void applyCorrection(const Matrix& deltas);
113 
114  /**
115  * Determines the robust error of the current candidate pose.
116  * @param weightedErrorVector Resulting (weighted - if using a robust estimator) error vector
117  * @param weightVector Vector holding the weights that have already been applied to the error vector
118  * @param invertedCovariances Optional 2x2 inverted covariance matrices
119  * @return The resulting robust error
120  * @tparam tEstimator The type of the estimator that is applied for error determination
121  */
122  template <Estimator::EstimatorType tEstimator>
123  Scalar determineRobustError(Matrix& weightedErrorVector, Matrix& weightVector, const Matrix* invertedCovariances) const;
124 
125  /**
126  * Accepts the current pose candidate as better model.
127  */
128  inline void acceptCorrection();
129 
130  protected:
131 
132  /// Universal model that will be optimized.
134 
135  /// Universal model that stores the most recent optimization result as candidate.
137 
138  /// The number of measurement elements that are used to optimize the model.
139  const size_t numberElements_;
140 
141  /// The value calculation callback function.
143 
144  /// The error calculation callback function.
146 
147  /// The Callback function allowing to transform the model into an external model before the value and error callback functions are invoked.
149 
150  /// The optional callback function allowing to adjust a model before it is accepted or rejected
152  };
153 
154  public:
155 
156  /**
157  * Optimizes a universal model by minimizing the error the model produces.
158  * @param model Universal model that will be optimized
159  * @param numberElements Number of elements (measurements) that are used to determine the optimized model
160  * @param valueCallback Callback function that is used to determine the value for an individual element (measurement) by application of the model
161  * @param errorCallback Callback function that is used to determine the error for an individual element (measurement)
162  * @param modelTransformationCallback Optional callback function allowing to transform the internal model into an extern model if intended
163  * @param modelAdjustmentCallback Optional callback function allowing to adjust the internal (already optimized) model before it will be checked on improvements
164  * @param optimizedModel Resulting optimized model
165  * @param iterations Number of iterations to be applied at most, if no convergence can be reached
166  * @param estimator Robust error estimator to be used
167  * @param lambda Initial Levenberg-Marquardt damping value which may be changed after each iteration using the damping factor, with range [0, infinity)
168  * @param lambdaFactor Levenberg-Marquardt damping factor to be applied to the damping value, with range [1, infinity)
169  * @param initialError Optional resulting averaged pixel error for the given initial parameters, in relation to the defined estimator
170  * @param finalError Optional resulting averaged pixel error for the final optimized parameters, in relation to the defined estimator
171  * @param intermediateErrors Optional resulting intermediate (improving) errors
172  * @return True, if the model could be optimized
173  */
174  static bool optimizeUniversalModel(const Model& model, const size_t numberElements, const ValueCallback& valueCallback, const ErrorCallback& errorCallback, const ModelTransformationCallback& modelTransformationCallback, const ModelAdjustmentCallback& modelAdjustmentCallback, Model& optimizedModel, const unsigned int iterations = 5u, const Estimator::EstimatorType estimator = Estimator::ET_SQUARE, Scalar lambda = Scalar(0.001), const Scalar lambdaFactor = Scalar(5), Scalar* initialError = nullptr, Scalar* finalError = nullptr, Scalars* intermediateErrors = nullptr);
175 };
176 
177 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize>
178 NonLinearUniversalOptimizationDense<tModelSize, tResultDimension, tExternalModelSize>::UniversalOptimizationProvider::UniversalOptimizationProvider(Model& model, const size_t numberElements, const ValueCallback& valueCallback, const ErrorCallback& errorCallback, const ModelTransformationCallback& modelTransformationCallback, const ModelAdjustmentCallback& modelAdjustmentCallback) :
179  model_(model),
180  numberElements_(numberElements),
181  valueCallback_(valueCallback),
182  errorCallback_(errorCallback),
183  modelTransformationCallback_(modelTransformationCallback),
184  modelAdjustmentCallback_(modelAdjustmentCallback)
185 {
186  ocean_assert(valueCallback_);
187  ocean_assert(errorCallback_);
188  ocean_assert(modelTransformationCallback_);
189 
190  candidateModel_ = model;
191 };
192 
193 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize>
195 {
196  ocean_assert(valueCallback_);
197  ocean_assert(modelTransformationCallback_);
198 
199  jacobian.resize(tResultDimension * numberElements_, tModelSize);
200 
201  const Scalar eps = Numeric::weakEps();
202  const Scalar invEps = Scalar(1) / eps;
203 
204  // transform the internal to the external model
205  ExternalModel externalModel;
206  modelTransformationCallback_(model_, externalModel);
207 
208  // stores individual models, each model with one individual epsilon offset
209  StaticBuffer<ExternalModel, tModelSize> externalEpsModels;
210  for (size_t a = 0; a < tModelSize; ++a)
211  {
212  Model internalModel = model_;
213  internalModel[a] += eps;
214 
215  modelTransformationCallback_(internalModel, externalEpsModels[a]);
216  }
217 
218  Result result, epsResult;
219  for (size_t n = 0; n < numberElements_; ++n)
220  {
221  // calculate the value for the current model
222  valueCallback_(externalModel, n, result);
223 
224  for (size_t m = 0; m < tModelSize; ++m)
225  {
226  // calculate the value for the epsilon model
227  valueCallback_(externalEpsModels[m], n, epsResult);
228 
229  // store the individual results
230  for (size_t d = 0; d < tResultDimension; ++d)
231  {
232  jacobian[n * tResultDimension + d][m] = (epsResult[d] - result[d]) * invEps;
233  }
234  }
235  }
236 }
237 
238 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize>
240 {
241  ocean_assert(deltas.rows() == tModelSize);
242 
243  for (size_t n = 0; n < tModelSize; ++n)
244  {
245  const Scalar& delta = deltas(n);
246  candidateModel_[n] = model_[n] - delta;
247  }
248 
249  if (modelAdjustmentCallback_)
250  {
251  modelAdjustmentCallback_(candidateModel_);
252  }
253 }
254 
255 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize>
256 template <Estimator::EstimatorType tEstimator>
258 {
259  ocean_assert(errorCallback_);
260  ocean_assert(modelTransformationCallback_);
261 
262  OCEAN_SUPPRESS_UNUSED_WARNING(invertedCovariances);
263  ocean_assert(invertedCovariances == nullptr);
264 
265  // set the correct size of the resulting error vector
266  weightedErrorVector.resize(tResultDimension * numberElements_, 1u);
267  Result* const weightedErrors = (Result*)weightedErrorVector.data();
268 
269  ExternalModel externalModel;
270  modelTransformationCallback_(candidateModel_, externalModel);
271 
272  Scalar sqrError = 0;
273  Scalars sqrErrors;
274 
275  if constexpr (!Estimator::isStandardEstimator<tEstimator>())
276  {
277  sqrErrors.reserve(numberElements_);
278  }
279 
280  for (size_t n = 0; n < numberElements_; ++n)
281  {
282  Result& weightedErrorPointer = *((Result*)weightedErrorVector.data() + n);
283  if (!errorCallback_(externalModel, n, weightedErrorPointer))
284  {
285  return Numeric::maxValue();
286  }
287 
288  if constexpr (Estimator::isStandardEstimator<tEstimator>())
289  {
290  sqrError += Numeric::summedSqr(weightedErrorPointer.data(), tResultDimension);
291  }
292  else
293  {
294  ocean_assert(!Estimator::isStandardEstimator<tEstimator>());
295  sqrErrors.emplace_back(Numeric::summedSqr(weightedErrorPointer.data(), tResultDimension));
296  }
297  }
298 
299  // check whether the standard estimator is used
300  if constexpr (Estimator::isStandardEstimator<tEstimator>())
301  {
302  // the weight vector should be and should stay invalid
303  ocean_assert(!weightVector);
304 
305  ocean_assert(numberElements_ > 0);
306  return sqrError /= Scalar(numberElements_);
307  }
308  else
309  {
310  // now we need the weight vector
311  weightVector.resize(tResultDimension * numberElements_, 1u);
312 
313  ocean_assert(sqrErrors.size() == numberElements_);
314 
315  return sqrErrors2robustErrors<tEstimator, tResultDimension>(sqrErrors, tModelSize, weightedErrors, (StaticBuffer<Scalar, tResultDimension>*)(weightVector.data()), nullptr);
316  }
317 }
318 
319 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize>
321 {
322  model_ = candidateModel_;
323 }
324 
325 template <unsigned int tModelSize, unsigned int tResultDimension, unsigned int tExternalModelSize>
326 bool NonLinearUniversalOptimizationDense<tModelSize, tResultDimension, tExternalModelSize>::optimizeUniversalModel(const Model& model, const size_t numberElements, const ValueCallback& valueCallback, const ErrorCallback& errorCallback, const ModelTransformationCallback& modelTransformationCallback, const ModelAdjustmentCallback& modelAdjustmentCallback, Model& optimizedModel, const unsigned int iterations, const Estimator::EstimatorType estimator, Scalar lambda, const Scalar lambdaFactor, Scalar* initialError, Scalar* finalError, Scalars* intermediateErrors)
327 {
328  ocean_assert(&model != &optimizedModel);
329  optimizedModel = model;
330 
331  UniversalOptimizationProvider provider(optimizedModel, numberElements, valueCallback, errorCallback, modelTransformationCallback, modelAdjustmentCallback);
332  return NonLinearOptimization::denseOptimization<UniversalOptimizationProvider>(provider, iterations, estimator, lambda, lambdaFactor, initialError, finalError, nullptr, intermediateErrors);
333 }
334 
335 }
336 
337 }
338 
339 #endif // META_OCEAN_GEOMETRY_NON_LINEAR_UNIVERSAL_OPTIMIZATION_H
This class implements a container for callback functions.
Definition: Callback.h:3456
EstimatorType
Definition of individual robust estimator types.
Definition: Estimator.h:34
@ ET_SQUARE
The standard square error estimator (L2).
Definition: Estimator.h:52
This class implements the base optimization provider.
Definition: NonLinearOptimization.h:293
This class implements the basic functions for least square or robust optimization algorithms for non ...
Definition: NonLinearOptimization.h:34
This class implements a dense universal optimization provider for universal models and measurement/da...
Definition: NonLinearUniversalOptimizationDense.h:88
const ModelAdjustmentCallback modelAdjustmentCallback_
The optional callback function allowing to adjust a model before it is accepted or rejected.
Definition: NonLinearUniversalOptimizationDense.h:151
void determineJacobian(Matrix &jacobian) const
Determines the jacobian matrix for the current camera.
Definition: NonLinearUniversalOptimizationDense.h:194
Model & model_
Universal model that will be optimized.
Definition: NonLinearUniversalOptimizationDense.h:133
const ErrorCallback errorCallback_
The error calculation callback function.
Definition: NonLinearUniversalOptimizationDense.h:145
void acceptCorrection()
Accepts the current pose candidate as better model.
Definition: NonLinearUniversalOptimizationDense.h:320
Scalar determineRobustError(Matrix &weightedErrorVector, Matrix &weightVector, const Matrix *invertedCovariances) const
Determines the robust error of the current candidate pose.
Definition: NonLinearUniversalOptimizationDense.h:257
void applyCorrection(const Matrix &deltas)
Applies the model correction and stores the new model as candidate.
Definition: NonLinearUniversalOptimizationDense.h:239
Model candidateModel_
Universal model that stores the most recent optimization result as candidate.
Definition: NonLinearUniversalOptimizationDense.h:136
const size_t numberElements_
The number of measurement elements that are used to optimize the model.
Definition: NonLinearUniversalOptimizationDense.h:139
const ModelTransformationCallback modelTransformationCallback_
The Callback function allowing to transform the model into an external model before the value and err...
Definition: NonLinearUniversalOptimizationDense.h:148
UniversalOptimizationProvider(Model &model, const size_t numberElements, const ValueCallback &valueCallback, const ErrorCallback &errorCallback, const ModelTransformationCallback &modelTransformationCallback, const ModelAdjustmentCallback &modelAdjustmentCallback)
Creates a new universal optimization object.
Definition: NonLinearUniversalOptimizationDense.h:178
const ValueCallback valueCallback_
The value calculation callback function.
Definition: NonLinearUniversalOptimizationDense.h:142
This class implements an optimization for universal dense problems with one model (optimization probl...
Definition: NonLinearUniversalOptimizationDense.h:32
Callback< bool, const ExternalModel &, const size_t, Result & > ErrorCallback
Definition of a callback function for dense error calculation.
Definition: NonLinearUniversalOptimizationDense.h:65
static bool optimizeUniversalModel(const Model &model, const size_t numberElements, const ValueCallback &valueCallback, const ErrorCallback &errorCallback, const ModelTransformationCallback &modelTransformationCallback, const ModelAdjustmentCallback &modelAdjustmentCallback, Model &optimizedModel, const unsigned int iterations=5u, const Estimator::EstimatorType estimator=Estimator::ET_SQUARE, Scalar lambda=Scalar(0.001), const Scalar lambdaFactor=Scalar(5), Scalar *initialError=nullptr, Scalar *finalError=nullptr, Scalars *intermediateErrors=nullptr)
Optimizes a universal model by minimizing the error the model produces.
Definition: NonLinearUniversalOptimizationDense.h:326
StaticBuffer< Scalar, tResultDimension > Result
Definition of a model result.
Definition: NonLinearUniversalOptimizationDense.h:48
Callback< void, Model & > ModelAdjustmentCallback
Definition of a dense model adjustment function.
Definition: NonLinearUniversalOptimizationDense.h:80
StaticBuffer< Scalar, tExternalModelSize > ExternalModel
Definition of an external model.
Definition: NonLinearUniversalOptimizationDense.h:43
StaticBuffer< Scalar, tModelSize > Model
Definition of a model.
Definition: NonLinearUniversalOptimizationDense.h:38
Callback< void, const ExternalModel &, const size_t, Result & > ValueCallback
Definition of a callback function for dense value calculation.
Definition: NonLinearUniversalOptimizationDense.h:56
Callback< void, Model &, ExternalModel & > ModelTransformationCallback
Definition of a dense model transformation function.
Definition: NonLinearUniversalOptimizationDense.h:73
const T * data() const
Returns a pointer to the internal values.
Definition: Matrix.h:798
size_t rows() const
Returns the count of rows.
Definition: Matrix.h:692
void resize(const size_t rows, const size_t columns)
Resizes this matrix.
static constexpr T weakEps()
Returns a weak epsilon.
static T summedSqr(const T *values, const size_t number)
Returns the summed squares of a given values.
Definition: Numeric.h:1514
static constexpr T maxValue()
Returns the max scalar value.
Definition: Numeric.h:3244
const T * data() const
Returns the buffer data pointer.
Definition: StaticBuffer.h:240
float Scalar
Definition of a scalar type.
Definition: Math.h:128
std::vector< Scalar > Scalars
Definition of a vector holding Scalar objects.
Definition: Math.h:144
The namespace covering the entire Ocean framework.
Definition: Accessor.h:15