Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpPointMap.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2024 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/vpPointMap.h>
32
33#ifdef VISP_HAVE_OPENMP
34#include <omp.h>
35#endif
36
37#define VP_RB_POINT_MAP_DEBUG 0
38
40
42{
43 X.resize(indices.getRows(), 3, false, false);
44 for (unsigned int i = 0; i < indices.getRows(); ++i) {
45 unsigned idx = indices[i][0];
46 X[i][0] = m_X[idx][0];
47 X[i][1] = m_X[idx][1];
48 X[i][2] = m_X[idx][2];
49 }
50}
51
53{
54 cX.resize(m_X.getRows(), 3, false, false);
55
56 const vpTranslationVector t = cTw.getTranslationVector();
57 const vpRotationMatrix R = cTw.getRotationMatrix();
58
59 vpMatrix::mult2Matrices(m_X, R.t(), cX);
60 for (unsigned int i = 0; i < m_X.getRows(); ++i) {
61 cX[i][0] += t[0];
62 cX[i][1] += t[1];
63 cX[i][2] += t[2];
64 }
65}
66
68{
69 vpMatrix X(indices.getRows(), 3);
70 for (unsigned int i = 0; i < indices.getRows(); ++i) {
71 unsigned idx = indices[i][0];
72 X[i][0] = m_X[idx][0];
73 X[i][1] = m_X[idx][1];
74 X[i][2] = m_X[idx][2];
75 }
76 cX.resize(indices.getRows(), 3, false, false);
77
78 const vpTranslationVector t = cTw.getTranslationVector();
79 const vpRotationMatrix R = cTw.getRotationMatrix();
80
81 vpMatrix::mult2Matrices(X, R.t(), cX);
82
83 for (unsigned int i = 0; i < indices.getRows(); ++i) {
84 cX[i][0] += t[0];
85 cX[i][1] += t[1];
86 cX[i][2] += t[2];
87 }
88}
89
91{
92 project(indices, cTw, cX);
93 xs.resize(cX.getRows(), 2, false, false);
94 for (unsigned int i = 0; i < cX.getRows(); ++i) {
95 xs[i][0] = cX[i][0] / cX[i][2];
96 xs[i][1] = cX[i][1] / cX[i][2];
97 }
98}
99
100void vpPointMap::project(const vpCameraParameters &cam, const vpArray2D<int> &indices, const vpHomogeneousMatrix &cTw, vpMatrix &cX, vpMatrix &xs, vpMatrix &uvs)
101{
103 throw vpException(vpException::badValue, "Only cameras without distortion are supported");
104 }
105 project(indices, cTw, cX, xs);
106 uvs.resize(xs.getRows(), xs.getCols(), false, false);
107 for (unsigned int i = 0; i < xs.getRows(); ++i) {
108 vpMeterPixelConversion::convertPointWithoutDistortion(cam, xs[i][0], xs[i][1], uvs[i][0], uvs[i][1]);
109 }
110}
111
112void vpPointMap::getVisiblePoints(const unsigned int h, const unsigned int w, const vpMatrix &cX, const vpMatrix &uvs, const vpColVector &expectedZ, std::vector<int> &indices)
113{
114 for (unsigned int i = 0; i < cX.getRows(); ++i) {
115 const double u = uvs[i][0], v = uvs[i][1];
116 const double Z = cX[i][2];
117 if (u < 0 || v < 0 || u >= w || v >= h) {
118 continue;
119 }
120 if (fabs(Z - expectedZ[i]) > m_maxDepthErrorVisible) {
121 continue;
122 }
123 indices.push_back(i);
124 }
125}
126
127void vpPointMap::getVisiblePoints(const unsigned int h, const unsigned int w, const vpCameraParameters &cam, const vpHomogeneousMatrix &cTw, const vpImage<float> &depth, std::vector<int> &indices)
128{
129 indices.clear();
130 const vpRotationMatrix cRw = cTw.getRotationMatrix();
131 const vpTranslationVector t = cTw.getTranslationVector();
132 vpMatrix cX(m_X.getRows(), m_X.getCols());
133 vpMatrix cN(m_normals.getRows(), m_normals.getCols());
134 const vpColVector co = cTw.getTranslationVector();
135
136 const vpColVector cameraRayObjectFrame = (cRw.inverse() * vpColVector({ 0.0, 0.0, 1.0 }));
137
138 vpMatrix::mult2Matrices(m_X, cRw.t(), cX);
139 if (m_normals.getRows() > 0) {
140 vpMatrix::mult2Matrices(m_normals, cRw.t(), cN);
141 }
142
143 std::vector<std::vector<int>> indicesPerThread;
144 const double normalThreshold = vpMath::rad(180 - m_normalThresholdVisible);
145
146#ifdef VISP_HAVE_OPENMP
147#pragma omp parallel
148#endif
149 {
150 vpColVector cameraRay(3);
151#ifdef VISP_HAVE_OPENMP
152#pragma omp single
153 {
154 unsigned int numThreads = omp_get_num_threads();
155 indicesPerThread.resize(numThreads);
156 }
157#else
158 {
159 indicesPerThread.resize(1);
160 }
161#endif
162
163#ifdef VISP_HAVE_OPENMP
164 unsigned int threadIdx = omp_get_thread_num();
165#else
166 unsigned int threadIdx = 0;
167#endif
168 std::vector<int> localIndices;
169 double u, v;
170
171#ifdef VISP_HAVE_OPENMP
172 localIndices.reserve(m_X.getRows() / omp_get_num_threads());
173#pragma omp for nowait
174#endif
175 for (int i = 0; i < static_cast<int>(m_X.getRows()); ++i) {
176
177
178 // Filter points that are behind the camera
179 const double Z = cX[i][2] + t[2];
180 if (Z <= 0.0) {
181 continue;
182 }
183 const double X = cX[i][0] + t[0], Y = cX[i][1] + t[1];
184
185 // Filter points that are on the other side of the object
186 if (m_normals.getRows() > 0) {
187 cameraRay = { X, Y, Z };
188 cameraRay.normalize();
189 double dotProd = cN[i][0] * cameraRay[0] + cN[i][1] * cameraRay[1] + cN[i][2] * cameraRay[2];
190 double angle = acos(dotProd);
191 if (angle < normalThreshold) {
192 continue;
193 }
194 }
195 const double x = X / Z, y = Y / Z;
196 vpMeterPixelConversion::convertPointWithoutDistortion(cam, x, y, u, v);
197 // Filter points outside of the image
198 if (u < 0 || v < 0 || u >= w || v >= h) {
199 continue;
200 }
201
202 unsigned int uint = static_cast<unsigned int>(u), vint = static_cast<unsigned int>(v);
203 // Filter points whose reprojection does not match the depth map: self occlusion when rendered depth map,
204 // occlusion or noise in the case of a true depth image
205 if (fabs(Z - depth[vint][uint]) > m_maxDepthErrorVisible) {
206 continue;
207 }
208 localIndices.push_back(i);
209 }
210 indicesPerThread[threadIdx] = std::move(localIndices);
211 }
212 for (const std::vector<int> &indicesPart: indicesPerThread) {
213 indices.insert(indices.end(), std::make_move_iterator(indicesPart.begin()), std::make_move_iterator(indicesPart.end()));
214 }
215}
216
217void vpPointMap::getOutliers(const vpArray2D<int> &originalIndices, const vpMatrix &uvs, const vpMatrix &observations, std::vector<int> &indices)
218{
219 if (uvs.getRows() != observations.getRows()) {
220 throw vpException(vpException::dimensionError, "Uvs and observations should have same number of rows");
221 }
222 indices.clear();
223 double thresholdSqr = vpMath::sqr(m_outlierThreshold);
224 for (unsigned int i = 0; i < uvs.getRows(); ++i) {
225 const double error = vpMath::sqr(uvs[i][0] - observations[i][0]) + vpMath::sqr(uvs[i][1] - observations[i][1]);
226 if (error >= thresholdSqr) {
227 indices.push_back(originalIndices[i][0]);
228 }
229 }
230}
231
233const vpMatrix &uvs, const vpImage<float> &modelDepth, const vpImage<float> &depth, const vpImage<vpRGBf> &normals,
234vpMatrix &oXs, vpMatrix &oNs, std::vector<int> &validCandidateIndices)
235{
236 if (originalIndices.getRows() != uvs.getRows()) {
237 throw vpException(vpException::dimensionError, "Indices and keypoint locations should have the same dimensions");
238 }
239 validCandidateIndices.clear();
240 double x, y;
241 vpColVector oX(3);
242 vpColVector cX(3);
243 const vpHomogeneousMatrix wTc = cTw.inverse();
244 const vpRotationMatrix wRc = wTc.getRotationMatrix();
246 double farEnoughThresholdSq = m_minDistNewPoint * m_minDistNewPoint;
247
248 std::vector<std::array<double, 3>> validoXList;
249 std::vector<std::array<double, 3>> validoNList;
250
251 validoXList.reserve(uvs.getRows());
252 if (normals.getSize() > 0) {
253 validoNList.reserve(uvs.getRows());
254 }
255
256 for (unsigned int i = 0; i < uvs.getRows(); ++i) {
257 double u = uvs[i][0], v = uvs[i][1];
258 unsigned int uint = static_cast<unsigned int>(u), vint = static_cast<unsigned int>(v);
259 double Z;
260 if (modelDepth.getSize() == 0) { // We are performing odometry or do not have a depth oracle
261 Z = static_cast<double>(depth[vint][uint]);
262 if (Z <= 0.0) {
263 continue;
264 }
265 }
266 else {
267 double renderZ = modelDepth[vint][uint];
268 if (renderZ <= 0.f) {
269 continue;
270 }
271 if (depth.getSize() > 0 && depth[vint][uint] > 0.f) { // Depth information from camera is available
272 Z = depth[vint][uint];
273 // Check if depth from model and camera match
274 if (m_maxDepthErrorCandidate > 0.0 && fabs(renderZ - Z) >= m_maxDepthErrorCandidate) {
275 continue;
276 }
277 }
278 Z = renderZ; // For addition, use the rendered depth
279 }
280
281 vpPixelMeterConversion::convertPointWithoutDistortion(cam, u, v, x, y);
282 cX[0] = x * Z;
283 cX[1] = y * Z;
284 cX[2] = Z;
285 oX = wRc * cX;
286 oX += t;
287
288 // Filter candidates that are too close to already existing points in the map and other points
289 bool isFarEnoughFromOtherPoints = true;
290 if (m_minDistNewPoint > 0.0) {
291 for (unsigned int j = 0; j < m_X.getRows(); ++j) {
292 double errSq = vpMath::sqr(oX[0] - m_X[j][0]) + vpMath::sqr(oX[1] - m_X[j][1]) + vpMath::sqr(oX[2] - m_X[j][2]);
293 if (errSq < farEnoughThresholdSq) {
294 isFarEnoughFromOtherPoints = false;
295 break;
296 }
297 }
298 if (isFarEnoughFromOtherPoints) {
299 for (const std::array<double, 3> &other: validoXList) {
300 double errSq = vpMath::sqr(oX[0] - other[0]) + vpMath::sqr(oX[1] - other[1]) + vpMath::sqr(oX[2] - other[2]);
301 if (errSq < farEnoughThresholdSq) {
302 isFarEnoughFromOtherPoints = false;
303 break;
304 }
305 }
306 }
307 }
308
309 if (isFarEnoughFromOtherPoints) {
310 validoXList.push_back({ oX[0], oX[1], oX[2] });
311 validCandidateIndices.push_back(originalIndices[i][0]);
312 if (normals.getSize() > 0) {
313 vpRGBf n = normals[vint][uint];
314 validoNList.push_back({ n.R, n.G, n.B });
315 }
316 }
317 }
318
319 oXs.resize(static_cast<unsigned int>(validoXList.size()), 3, false, false);
320 oNs.resize(static_cast<unsigned int>(validoNList.size()), 3, false, false);
321
322 unsigned int i = 0;
323 for (const std::array<double, 3> &oX: validoXList) {
324 oXs[i][0] = oX[0];
325 oXs[i][1] = oX[1];
326 oXs[i][2] = oX[2];
327 ++i;
328 }
329 i = 0;
330 for (const std::array<double, 3> &oN: validoNList) {
331 oNs[i][0] = oN[0];
332 oNs[i][1] = oN[1];
333 oNs[i][2] = oN[2];
334 ++i;
335 }
336}
337
339{
340 m_X = vpMatrix();
341 m_normals = vpMatrix();
342}
343
344vpMatrix removeAndAdd(const vpMatrix &oldArray, unsigned int newSize, const std::vector<int> &removedIndices, const vpMatrix &rowsToAdd, unsigned int &numAddedPoints)
345{
346 unsigned int numCols = 3;
347 vpMatrix newX(newSize, numCols);
348 unsigned int newXIndex = 0;
349 unsigned int oldXIndex = 0;
350
351 // Copy between removed rows
352 for (int removedRow : removedIndices) {
353#if VP_RB_POINT_MAP_DEBUG
354 if (removedRow >= static_cast<int>(oldArray.getRows())) {
355 throw vpException(vpException::dimensionError, "Removed row is out of bounds");
356 }
357#endif
358 unsigned int copiedRows = removedRow - oldXIndex;
359 if (copiedRows > 0) {
360 memcpy(newX[newXIndex], oldArray[oldXIndex], copiedRows * numCols * sizeof(double));
361 newXIndex += copiedRows;
362 }
363 oldXIndex = removedRow + 1;
364 }
365 // Copy from last removed row to the end of the array
366 unsigned int copiedRows = oldArray.getRows() - oldXIndex;
367 if (copiedRows > 0) {
368 memcpy(newX[newXIndex], oldArray[oldXIndex], copiedRows * numCols * sizeof(double));
369 newXIndex += copiedRows;
370 }
371
372 numAddedPoints = std::min(rowsToAdd.getRows(), static_cast<unsigned int>(newSize) - newXIndex);
373 memcpy(newX[newXIndex], rowsToAdd[0], numAddedPoints * numCols *sizeof(double));
374 return newX;
375}
376
377void vpPointMap::updatePoints(const vpArray2D<int> &indicesToRemove, const vpMatrix &pointsToAdd, const vpMatrix &normalsToAdd,
378 std::vector<int> &removedIndices, unsigned int &numAddedPoints)
379{
380 removedIndices.clear();
381 if (normalsToAdd.getRows() > 0 && normalsToAdd.getRows() != pointsToAdd.getRows()) {
382 throw vpException(vpException::dimensionError, "Adding normal data to point map, but number of points and normals do not match");
383 }
384 int newSize = m_X.getRows() - indicesToRemove.getRows() + pointsToAdd.getRows();
385 for (unsigned int i = 0; i < indicesToRemove.getRows(); ++i) {
386 removedIndices.push_back(indicesToRemove[i][0]);
387 }
388
389 int maxPoints = static_cast<int>(m_maxPoints);
390 std::sort(removedIndices.begin(), removedIndices.end());
391 if (newSize > maxPoints) {
392 int shouldBeRemoved = newSize - maxPoints;
393 newSize = maxPoints;
394
395 // If the first values are filtered by indicesToRemove, we need to further increment the start index
396 std::vector<int> startingIndices;
397 auto removedIt = removedIndices.begin();
398 int i = 0;
399 int n_rows = static_cast<int>(m_X.getRows());
400 while ((startingIndices.size() < static_cast<size_t>(shouldBeRemoved)) && (i < n_rows)) {
401 if (removedIt == removedIndices.end() || i < (*removedIt)) {
402 startingIndices.push_back(i);
403 }
404 else {
405 ++removedIt;
406 }
407 ++i;
408 }
409
410 removedIndices.insert(removedIndices.begin(), startingIndices.begin(), startingIndices.end());
411 std::sort(removedIndices.begin(), removedIndices.end());
412 }
413
414 m_X = removeAndAdd(m_X, newSize, removedIndices, pointsToAdd, numAddedPoints);
415 if (normalsToAdd.getRows() > 0 || m_normals.getRows() > 0) {
416 m_normals = removeAndAdd(m_normals, newSize, removedIndices, normalsToAdd, numAddedPoints);
417 }
418 if (m_normals.getRows() > 0 && m_X.size() != m_normals.size()) {
419 throw vpException(vpException::dimensionError, "Mismatch between number of points and normals");
420 }
421
422}
423
424END_VISP_NAMESPACE
Implementation of a generic 2D array used as base class for matrices and vectors.
Definition vpArray2D.h:146
unsigned int getCols() const
Definition vpArray2D.h:423
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition vpArray2D.h:448
unsigned int getRows() const
Definition vpArray2D.h:433
Generic class defining intrinsic camera parameters.
@ perspectiveProjWithoutDistortion
Perspective projection without distortion model.
Implementation of column vector and the associated operations.
vpColVector & normalize()
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
@ dimensionError
Bad dimension.
Definition vpException.h:71
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpRotationMatrix getRotationMatrix() const
vpTranslationVector getTranslationVector() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getSize() const
Definition vpImage.h:221
static double rad(double deg)
Definition vpMath.h:129
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)
void getOutliers(const vpArray2D< int > &originalIndices, const vpMatrix &uvs, const vpMatrix &observations, std::vector< int > &indices)
void project(const vpHomogeneousMatrix &cTw, vpMatrix &cX)
void selectValidNewCandidates(const vpCameraParameters &cam, const vpHomogeneousMatrix &cTw, const vpArray2D< int > &originalIndices, const vpMatrix &uvs, const vpImage< float > &modelDepth, const vpImage< float > &depth, const vpImage< vpRGBf > &normals, vpMatrix &oXs, vpMatrix &oNs, std::vector< int > &validCandidateIndices)
void getVisiblePoints(const unsigned int h, const unsigned int w, const vpMatrix &cX, const vpMatrix &uvs, const vpColVector &expectedZ, std::vector< int > &indices)
const vpMatrix & getPoints()
Definition vpPointMap.h:87
void updatePoints(const vpArray2D< int > &indicesToRemove, const vpMatrix &pointsToAdd, const vpMatrix &normalsToAdd, std::vector< int > &removedIndices, unsigned int &numAddedPoints)
float B
Blue component.
Definition vpRGBf.h:161
float G
Green component.
Definition vpRGBf.h:160
float R
Red component.
Definition vpRGBf.h:159
Implementation of a rotation matrix and operations on such kind of matrices.
vpRotationMatrix t() const
vpRotationMatrix inverse() const
Class that consider the case of a translation vector.