Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpRBVisualOdometryUtils.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 */
30
31#include <visp3/rbt/vpRBVisualOdometryUtils.h>
32#include <visp3/core/vpImageFilter.h>
33
34#include <visp3/core/vpMatrix.h>
35#include <visp3/rbt/vpRBFeatureTrackerInput.h>
36#include <vector>
37#include <utility>
38
39#include <visp3/core/vpRobust.h>
40#include <visp3/core/vpExponentialMap.h>
41#include <visp3/core/vpHomogeneousMatrix.h>
42
44
45std::pair<std::vector<unsigned int>, std::vector<unsigned int>>
47 const vpRBFeatureTrackerInput &frame,
48 double minMaskConfidence, double minEdgeDistObject,
49 double minEdgeDistEnv)
50{
51 std::vector<unsigned int> objIndices, envIndices;
52 const vpImage<float> &renderDepth = frame.renders.depth;
53 const unsigned int h = renderDepth.getHeight(), w = renderDepth.getWidth();
54 const bool useMask = frame.hasMask() && minMaskConfidence > 0.0;
55 const double thresholdObject = vpMath::sqr(minEdgeDistObject), thresholdEnv = vpMath::sqr(minEdgeDistEnv);
56
57 std::vector<std::pair<double, double>> actualSilhouettePoints;
58 actualSilhouettePoints.reserve(frame.silhouettePoints.size());
59 for (const vpRBSilhouettePoint &p: frame.silhouettePoints) {
60 if (p.isSilhouette) {
61 actualSilhouettePoints.push_back(std::make_pair(
62 static_cast<double>(p.i), static_cast<double>(p.j)
63 ));
64 }
65 }
66
67 const auto testDistanceEdge = [&actualSilhouettePoints](double u, double v, double threshold) -> bool {
68 for (const std::pair<double, double> &p: actualSilhouettePoints) {
69 double dist2 = vpMath::sqr(p.first - v) + vpMath::sqr(p.second - u);
70 if (dist2 < threshold) {
71 return false;
72 }
73 }
74 return true;
75 };
76
77 for (unsigned int i = 0; i < keypoints.getRows(); ++i) {
78 double u = keypoints[i][0], v = keypoints[i][1];
79 if (u < 2.0 || v < 2.0 || u >= w - 2 || v >= h - 2) {
80 continue;
81 }
82 const unsigned int ui = static_cast<unsigned int>(u), vi = static_cast<unsigned int>(v);
83 const double Z = static_cast<double>(renderDepth[vi][ui]);
84
85 if (Z > 0.0) { // Potential object candidate
86 double maskValue = 1.f;
87 if (useMask) {
88 maskValue = 0.f;
89 for (int ki = -1; ki <= 1; ++ki) {
90 for (int kj = -1; kj <= 1; ++kj) {
91 maskValue += frame.mask[vi + ki][ui + kj];
92 }
93 }
94 maskValue /= 9.f;
95 }
96 if (!useMask || maskValue >= minMaskConfidence) {
97 bool notTooCloseToEdge = thresholdObject <= 0 || testDistanceEdge(u, v, thresholdObject);
98 if (notTooCloseToEdge) {
99 objIndices.push_back(i);
100 }
101 }
102 }
103 else { // Env candidate
104 bool notTooCloseToEdge = thresholdEnv <= 0 || testDistanceEdge(u, v, thresholdEnv);
105 if (notTooCloseToEdge) {
106 envIndices.push_back(i);
107 }
108 }
109 }
110 return std::make_pair(objIndices, envIndices);
111}
112
114 const vpLevenbergMarquardtParameters &parameters,
116{
117 vpMatrix L(points3d.getRows() * 2, 6);
118 vpMatrix Lt(6, points3d.getRows());
119 vpColVector e(points3d.getRows() * 2);
120 vpColVector weights(points3d.getRows() * 2);
121 vpColVector weighted_error(points3d.getRows() * 2);
122
123 if (points3d.getRows() != observations.getRows()) {
124 throw vpException(vpException::dimensionError, "Expected number of 3D points and 2D observations to be the same");
125 }
126 vpMatrix points3dTranspose = points3d.t();
127 vpMatrix cXt(points3d.getRows(), points3d.getCols());
128 vpMatrix Id(6, 6);
129 Id.eye();
130 vpRobust robust;
131 vpMatrix H(6, 6);
132 double mu = parameters.muInit;
133 double errorNormPrev = std::numeric_limits<double>::max();
134 for (unsigned int iter = 0; iter < parameters.maxNumIters; ++iter) {
135 const vpTranslationVector t = cTw.getTranslationVector();
136 const vpRotationMatrix cRw = cTw.getRotationMatrix();
137 vpMatrix::mult2Matrices(cRw, points3dTranspose, cXt);
138 // Project 3D points and compute interaction matrix
139#ifdef VISP_HAVE_OPENMP
140#pragma omp parallel for
141#endif
142 for (int i = 0; i < static_cast<int>(points3d.getRows()); ++i) {
143 const double X = cXt[0][i] + t[0], Y = cXt[1][i] + t[1], Z = cXt[2][i] + t[2];
144 const double x = X / Z, y = Y / Z;
145 e[i * 2] = x - observations[i][0];
146 e[i * 2 + 1] = y - observations[i][1];
147
148 L[i * 2][0] = -1.0 / Z; L[i * 2][1] = 0.0; L[i * 2][2] = x / Z;
149 L[i * 2][3] = x * y; L[i * 2][4] = -(1.0 + x * x); L[i * 2][5] = y;
150
151 L[i * 2 + 1][0] = 0.0; L[i * 2 + 1][1] = -1.0 / Z; L[i * 2 + 1][2] = y / Z;
152 L[i * 2 + 1][3] = 1.0 + y * y; L[i * 2 + 1][4] = -(x * y); L[i * 2 + 1][5] = -x;
153 }
154
155 robust.MEstimator(vpRobust::TUKEY, e, weights);
156 for (unsigned int i = 0; i < e.getRows(); ++i) {
157 weighted_error[i] = e[i] * weights[i];
158 for (unsigned int j = 0; j < 6; ++j) {
159 L[i][j] *= weights[i];
160 }
161 }
162
163 L.transpose(Lt);
164 L.AtA(H);
165 vpColVector Lte = Lt * weighted_error;
166 vpColVector v = -parameters.gain * ((H + Id * mu).pseudoInverse() * Lte);
167 cTw = vpExponentialMap::direct(v).inverse() * cTw;
168 double errorNormCurr = weighted_error.frobeniusNorm();
169 double improvementFactor = errorNormCurr / errorNormPrev;
170 if (iter > 0) {
171 if (improvementFactor < 1.0 && improvementFactor >(1.0 - parameters.minImprovementFactor)) {
172 break;
173 }
174 }
175 // if (improvementFactor < 1.0) {
176 mu *= parameters.muIterFactor;
177 // }
178 // else {
179 // mu /= parameters.muIterFactor;
180 // }
181 errorNormPrev = errorNormCurr;
182 }
183}
184
185
187 const vpLevenbergMarquardtParameters &parameters,
189{
190 vpMatrix L(points3d.getRows() * 3, 6, 0.0);
191 vpMatrix Lt(6, points3d.getRows());
192 vpColVector e(points3d.getRows() * 3);
193 vpColVector weights(points3d.getRows() * 3);
194 vpColVector weighted_error(points3d.getRows() * 3);
195
196 if (points3d.getRows() != observations.getRows()) {
197 throw vpException(vpException::dimensionError, "Expected number of 3D points and 2D observations to be the same");
198 }
199 vpMatrix points3dTranspose = points3d.t();
200 vpMatrix cXt(points3d.getRows(), points3d.getCols());
201 vpMatrix Id(6, 6);
202 Id.eye();
203 vpRobust robust;
204 vpMatrix H(6, 6);
205 double mu = parameters.muInit;
206 double errorNormPrev = std::numeric_limits<double>::max();
207 for (unsigned int iter = 0; iter < parameters.maxNumIters; ++iter) {
208 const vpTranslationVector t = cTw.getTranslationVector();
209 const vpRotationMatrix cRw = cTw.getRotationMatrix();
210 vpMatrix::mult2Matrices(cRw, points3dTranspose, cXt);
211 // Project 3D points and compute interaction matrix
212#ifdef VISP_HAVE_OPENMP
213#pragma omp parallel for
214#endif
215 for (int i = 0; i < static_cast<int>(points3d.getRows()); ++i) {
216 const double X = cXt[0][i] + t[0], Y = cXt[1][i] + t[1], Z = cXt[2][i] + t[2];
217
218 e[i * 3] = X - observations[i][0];
219 e[i * 3 + 1] = Y - observations[i][1];
220 e[i * 3 + 2] = Z - observations[i][2];
221
222 L[i * 3][0] = -1;
223 L[i * 3 + 1][1] = -1;
224 L[i * 3 + 2][2] = -1;
225
226 L[i * 3][4] = -Z;
227 L[i * 3][5] = Y;
228
229 L[i * 3 + 1][3] = Z;
230 L[i * 3 + 1][5] = -X;
231
232 L[i * 3 + 2][3] = -Y;
233 L[i * 3 + 2][4] = X;
234 }
235
236 robust.MEstimator(vpRobust::TUKEY, e, weights);
237 for (unsigned int i = 0; i < e.getRows(); ++i) {
238 weighted_error[i] = e[i] * weights[i];
239 for (unsigned int j = 0; j < 6; ++j) {
240 L[i][j] *= weights[i];
241 }
242 }
243
244 L.transpose(Lt);
245 L.AtA(H);
246 vpColVector Lte = Lt * weighted_error;
247 vpColVector v = -parameters.gain * ((H + Id * mu).pseudoInverse() * Lte);
248 cTw = vpExponentialMap::direct(v).inverse() * cTw;
249 double errorNormCurr = weighted_error.frobeniusNorm();
250 double improvementFactor = errorNormCurr / errorNormPrev;
251 if (iter > 0) {
252 if (improvementFactor < 1.0 && improvementFactor >(1.0 - parameters.minImprovementFactor)) {
253 break;
254 }
255 }
256 // if (improvementFactor < 1.0) {
257 mu *= parameters.muIterFactor;
258 // }
259 // else {
260 // mu /= parameters.muIterFactor;
261 // }
262 errorNormPrev = errorNormCurr;
263 }
264}
265
266END_VISP_NAMESPACE
unsigned int getCols() const
Definition vpArray2D.h:423
unsigned int getRows() const
Definition vpArray2D.h:433
Implementation of column vector and the associated operations.
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ dimensionError
Bad dimension.
Definition vpException.h:71
static vpHomogeneousMatrix direct(const vpColVector &v)
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix inverse() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getWidth() const
Definition vpImage.h:242
unsigned int getHeight() const
Definition vpImage.h:181
static double sqr(double x)
Definition vpMath.h:203
Implementation of a matrix and operations on matrices.
Definition vpMatrix.h:175
static void mult2Matrices(const vpMatrix &A, const vpMatrix &B, vpMatrix &C)
vpMatrix t() const
All the data related to a single tracking frame. This contains both the input data (from a real camer...
std::vector< vpRBSilhouettePoint > silhouettePoints
vpImage< float > mask
depth image, 0 sized if depth is not available
vpRBRenderData renders
camera parameters
Silhouette point simple candidate representation.
static void levenbergMarquardtKeypoints2D(const vpMatrix &points3d, const vpMatrix &observations, const vpLevenbergMarquardtParameters &parameters, vpHomogeneousMatrix &cTw)
static std::pair< std::vector< unsigned int >, std::vector< unsigned int > > computeIndicesObjectAndEnvironment(const vpMatrix &keypoints, const vpRBFeatureTrackerInput &frame, double minMaskConfidence, double minEdgeDistObject, double minEdgeDistEnv)
Compute the indices of the keypoints that belong to either the object (the renderered depth is greate...
static void levenbergMarquardtKeypoints3D(const vpMatrix &points3d, const vpMatrix &observations, const vpLevenbergMarquardtParameters &parameters, vpHomogeneousMatrix &cTw)
Contains an M-estimator and various influence function.
Definition vpRobust.h:84
@ TUKEY
Tukey influence function.
Definition vpRobust.h:89
void MEstimator(const vpRobustEstimatorType method, const vpColVector &residues, vpColVector &weights)
Definition vpRobust.cpp:130
Implementation of a rotation matrix and operations on such kind of matrices.
Class that consider the case of a translation vector.
vpImage< float > depth
Image containing the per-pixel normal vector (RGB, in object space).