Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpRBProbabilistic3DDriftDetector.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/vpRBProbabilistic3DDriftDetector.h>
32
33#include <visp3/core/vpRect.h>
34#include <visp3/core/vpPixelMeterConversion.h>
35#include <visp3/core/vpMeterPixelConversion.h>
36#include <visp3/core/vpDisplay.h>
37
38#include <visp3/rbt/vpRBFeatureTracker.h>
39
40#if defined(VISP_HAVE_NLOHMANN_JSON)
41#include VISP_NLOHMANN_JSON(json.hpp)
42#endif
43
45
47{
48 double score = 0.0;
50 if (m_points.size() == 0) {
51 return 1.0;
52 }
53 // Step 0: project all points
54#ifdef VISP_HAVE_OPENMP
55#pragma omp parallel for
56#endif
57 for (int i = 0; i < static_cast<int>(m_points.size()); ++i) {
58 vpStored3DSurfaceColorPoint &p = m_points[i];
59 p.update(cTo, frame.renders.cMo, frame.cam);
60 }
61
62
63
64
65 // Step 1: gather points visible in both images and in render
66
67 std::vector<vpStored3DSurfaceColorPoint *> visiblePoints;
68#ifdef VISP_HAVE_OPENMP
69#pragma omp parallel
70#endif
71 {
72 std::vector<vpStored3DSurfaceColorPoint *> visiblePointsLocal;
73#ifdef VISP_HAVE_OPENMP
74#pragma omp for
75#endif
76 for (int i = 0; i < static_cast<int>(m_points.size()); ++i) {
77 vpStored3DSurfaceColorPoint &p = m_points[i];
78 p.visible = true;
79 if (
80 p.projRenderPx[0] < 2 || static_cast<unsigned int>(p.projRenderPx[0]) >= frame.IRGB.getWidth() - 2
81 || p.projRenderPx[1] < 2 || static_cast<unsigned int>(p.projRenderPx[1]) >= frame.IRGB.getHeight() - 2
82 || p.projCurrPx[0] < 2 || static_cast<unsigned int>(p.projCurrPx[0]) >= frame.IRGB.getWidth() - 2
83 || p.projCurrPx[1] < 2 || static_cast<unsigned int>(p.projCurrPx[1]) >= frame.IRGB.getHeight() - 2) {
84 p.visible = false; // Point is outside of either current or previous image, ignore it
85 continue;
86 }
87
88 float ZrenderMap = frame.renders.depth[p.projRenderPx[1]][p.projRenderPx[0]];
89 // Version 2: compare previous projection with render, this does not filter occlusions
90 if (ZrenderMap == 0.f || fabs(p.renderX[2] - ZrenderMap) > m_maxError3D) {
91 p.visible = false;
92 continue;
93 }
94 // Filter occlusions if depth is available
95 bool validZMap = frame.hasDepth() && frame.depth[p.projCurrPx[1]][p.projCurrPx[0]] > 0.f;
96
97 if (validZMap) {
98 float actualZ = frame.depth[p.projCurrPx[1]][p.projCurrPx[0]];
99 // Filter against the specified Z distribution: depth that is too close to the camera wrt to the object render
100 // and is not in the Z distribution can be considered as an occlusion
101 if (p.currX[2] - actualZ > 3.f * m_depthSigma) {
102 p.visible = false;
103 continue;
104 }
105 }
106
107 // vpRGBf normalObject = frame.renders.normals[p.projRenderPx[1]][p.projRenderPx[0]];
108
109 // vpColVector cameraRay({ t[0] - p.X[0], t[1] - p.X[1], t[2] - p.X[2] });
110
111 // cameraRay.normalize();
112 // double angle = acos(vpColVector::dotProd(vpColVector({ normalObject.R, normalObject.G, normalObject.B }).normalize(), cameraRay));
113 // if (angle > vpMath::rad(85)) {
114 // p.visible = false;
115 // continue;
116 // }
117
118 // Filter points that are too close to the silhouette edges
119 if (frame.silhouettePoints.size() > 0) {
120 for (const vpRBSilhouettePoint &sp: frame.silhouettePoints) {
121 if (std::pow(static_cast<double>(sp.i) - p.projRenderPx[1], 2) + std::pow(static_cast<double>(sp.j) - p.projRenderPx[0], 2) < vpMath::sqr(2)) {
122 p.visible = false;
123 break;
124 }
125 }
126 }
127 // Version 3: could be using version 1 and 2. If 1 is wrong but 2 is ok, then there is an issue that is not self occlusion
128 // We could reweigh the error by the number of problematic points
129 // ...
130
131 if (p.visible) {
132 visiblePointsLocal.push_back(&p);
133 }
134 }
135#ifdef VISP_HAVE_OPENMP
136#pragma omp critical
137#endif
138 {
139 visiblePoints.insert(visiblePoints.end(), visiblePointsLocal.begin(), visiblePointsLocal.end());
140 }
141 }
142 if (visiblePoints.size() > 0) {
143 bool useMedian = false;
144
145 std::vector<double> scores;
146 scores.reserve(visiblePoints.size());
147 score = 0.0;
148 double weightSum = 0.0;
149
150#ifdef VISP_HAVE_OPENMP
151#pragma omp parallel
152#endif
153 {
154 std::vector<double> scoresLocal;
155 double weightSumLocal = 0.0;
156 double scoreLocal = 0.0;
157#ifdef VISP_HAVE_OPENMP
158#pragma omp for
159#endif
160 for (int i = 0; i < static_cast<int>(visiblePoints.size()); ++i) {
161 vpStored3DSurfaceColorPoint *p = visiblePoints[i];
162
163 const bool hasCorrectDepth = frame.hasDepth() && frame.depth[p->projCurrPx[1]][p->projCurrPx[0]] > 0.f;
164 const double Z = hasCorrectDepth ? frame.depth[p->projCurrPx[1]][p->projCurrPx[0]] : 0.0;
165 double depthError = Z > 0 ? fabs(p->currX[2] - Z) : 0.0;
166 double probaDepth = 1.0;
167 double scaleFactor = p->stats.covarianceScaleFactor();
168 double weight = 1.0 - std::min(1.0, scaleFactor / std::pow((m_initialColorSigma + 20.0), 2));
169 weightSumLocal += weight;
170
171
172 if (hasCorrectDepth) {
173 probaDepth = 1.0 - erf((depthError) / (m_depthSigma * sqrt(2.0)));
174 }
175
176 vpRGBf averageColor(0.f, 0.f, 0.f);
177
178 for (int j = -1; j < 2; ++j) {
179 for (int k = -1; k < 2; ++k) {
180 const vpRGBa currentColor = frame.IRGB[p->projCurrPx[1] + j][p->projCurrPx[0] + k];
181 averageColor.R += static_cast<float>(currentColor.R);
182 averageColor.G += static_cast<float>(currentColor.G);
183 averageColor.B += static_cast<float>(currentColor.B);
184
185 // const vpRGBf c(static_cast<float>(currentColor.R), static_cast<float>(currentColor.G), static_cast<float>(currentColor.B));
186 // double probaColor = p->stats.probability(c);
187 // if (probaColor > bestColorProba) {
188 // bestColorProba = probaColor;
189 // bestColor = c;
190 // }
191 }
192 }
193 averageColor = averageColor * (1.f / 9.f);
194
195 const double proba = p->stats.probability(averageColor) * probaDepth;
196
197 scoresLocal.push_back(proba); // Keep only the weight
198 scoreLocal += proba * weight;
199 p->updateColor(averageColor, static_cast<float>(m_colorUpdateRate * probaDepth));
200 }
201#ifdef VISP_HAVE_OPENMP
202#pragma omp critical
203#endif
204 {
205 scores.insert(scores.end(), scoresLocal.begin(), scoresLocal.end());
206 score += scoreLocal;
207 weightSum += weightSumLocal;
208 }
209 }
210 if (!useMedian) {
211 // Use average score, may be more sensitive to outliers
212 if (weightSum > 0.0) {
213 score /= weightSum;
214 }
215 else {
216 score = vpMath::getMean(scores);
217 }
218 return score;
219 }
220 else {
221 return vpMath::getMedian(scores);
222 }
223 }
224 else {
225 return 0.0;
226 }
227}
228
229
231 const vpRBFeatureTrackerInput &frame,
232 const vpHomogeneousMatrix &cTo, const vpHomogeneousMatrix &cprevTo)
233{
234 // const vpHomogeneousMatrix &cprevTo = frame.renders.cMo;
235 m_score = score(frame, cTo);
236
237 // Step 4: Sample bb to add new visible points
238 const vpHomogeneousMatrix oMcprev = cprevTo.inverse();
239 vpColVector cX(4, 1.0), renderX(4, 1.0);
240 vpColVector oX(4, 1.0);
241
242 const unsigned int h = frame.renders.depth.getHeight();
243 const unsigned int w = frame.renders.depth.getWidth();
244
245
246 const vpHomogeneousMatrix cprevTrender = cprevTo * frame.renders.cMo.inverse();
247
248 const unsigned int top = static_cast<unsigned int>(std::max(0.0, frame.renders.boundingBox.getTop()));
249 const unsigned int left = static_cast<unsigned int>(std::max(0.0, frame.renders.boundingBox.getLeft()));
250 const unsigned int bottom = std::min(h, static_cast<unsigned int>(frame.renders.boundingBox.getBottom()));
251 const unsigned int right = std::min(w, static_cast<unsigned int>(frame.renders.boundingBox.getRight()));
252
253 for (unsigned int i = top; i < bottom; i += m_sampleStep) {
254 for (unsigned int j = left; j < right; j += m_sampleStep) {
255 double u = static_cast<double>(j), v = static_cast<double>(i);
256 double x = 0.0, y = 0.0;
257 double Z = static_cast<double>(frame.renders.depth[i][j]);
258
259 if (Z > 0.0) {
260 vpPixelMeterConversion::convertPoint(frame.cam, u, v, x, y);
261 renderX[0] = x * Z;
262 renderX[1] = y * Z;
263 renderX[2] = Z;
264 cX = cprevTrender * renderX;
265 x = cX[0] / cX[2], y = cX[1] / cX[2];
266 vpMeterPixelConversion::convertPoint(frame.cam, x, y, u, v);
267 int prevI = static_cast<int>(v), prevJ = static_cast<int>(u);
268 if (prevI < 0 || prevI >= static_cast<int>(previousFrame.IRGB.getHeight()) || prevJ < 0 || prevJ >= static_cast<int>(previousFrame.IRGB.getWidth())) {
269 continue;
270 }
271 oX = oMcprev * cX;
273 newPoint.X[0] = oX[0] / oX[3];
274 newPoint.X[1] = oX[1] / oX[3];
275 newPoint.X[2] = oX[2] / oX[3];
276 const vpRGBa &c = previousFrame.IRGB[prevI][prevJ];
277 const float colorVariance = std::pow(static_cast<float>(m_initialColorSigma), 2.f);
278 newPoint.stats.init(vpRGBf(c.R, c.G, c.B), vpRGBf(colorVariance));
279 bool canAdd = true;
280 for (const vpStored3DSurfaceColorPoint &p : m_points) {
281 if (p.squaredDist(newPoint.X) < vpMath::sqr(m_minDist3DNewPoint)) {
282 canAdd = false;
283 break;
284 }
285 }
286 if (canAdd) {
287 m_points.push_back(newPoint);
288 }
289 }
290 }
291 }
292
293}
294
296{
297 for (const vpStored3DSurfaceColorPoint &p : m_points) {
298 if (p.visible) {
299 const vpRGBf color = p.stats.mean;
300 vpDisplay::displayPoint(I, p.projCurrPx[1], p.projCurrPx[0],
301 vpColor(static_cast<unsigned char>(color.R), static_cast<unsigned char>(color.G), static_cast<unsigned char>(color.B)), 3);
302 }
303 }
304}
305
307{
308 return m_score;
309}
310
312{
313 return m_score < 0.2;
314}
315
316#if defined(VISP_HAVE_NLOHMANN_JSON)
318{
319 setColorUpdateRate(j.value("colorUpdateRate", m_colorUpdateRate));
320 setDepthStandardDeviation(j.value("depthSigma", m_depthSigma));
321 setFilteringMax3DError(j.value("filteringMaxDistance", m_maxError3D));
322 setInitialColorStandardDeviation(j.value("initialColorSigma", m_initialColorSigma));
323 setMinDistForNew3DPoints(j.value("minDistanceNewPoints", m_minDist3DNewPoint));
324 setSampleStep(j.value("sampleStep", m_sampleStep));
325}
326
328{
329 std::ifstream f(filename);
330 if (!f.good()) {
331 throw vpException(vpException::ioError, "Could not open file %s", filename.c_str());
332 }
333
334 nlohmann::json j = nlohmann::json::parse(f);
335 f.close();
336 m_points = j;
337}
338void vpRBProbabilistic3DDriftDetector::saveRepresentation(const std::string &filename) const
339{
340 nlohmann::json j = m_points;
341 std::ofstream f(filename);
342 if (!f.good()) {
343 throw vpException(vpException::ioError, "Could not open file %s", filename.c_str());
344 }
345 f << j.dump(2);
346 f.close();
347}
348
349#endif
350
351END_VISP_NAMESPACE
Implementation of column vector and the associated operations.
Class to define RGB colors available for display functionalities.
Definition vpColor.h:157
static void displayPoint(const vpImage< unsigned char > &I, const vpImagePoint &ip, const vpColor &color, unsigned int thickness=1)
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ ioError
I/O error.
Definition vpException.h:67
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpHomogeneousMatrix inverse() const
vpTranslationVector getTranslationVector() 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 getMedian(const std::vector< double > &v)
Definition vpMath.cpp:343
static double sqr(double x)
Definition vpMath.h:203
static double getMean(const std::vector< double > &v)
Definition vpMath.cpp:323
static void convertPoint(const vpCameraParameters &cam, const double &x, const double &y, double &u, double &v)
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
All the data related to a single tracking frame. This contains both the input data (from a real camer...
vpImage< vpRGBa > IRGB
Image luminance.
std::vector< vpRBSilhouettePoint > silhouettePoints
vpImage< float > depth
RGB image, 0 sized if RGB is not available.
vpRBRenderData renders
camera parameters
double getScore() const VP_OVERRIDE
Returns the probability [0, 1] that tracking is successful.
double score(const vpRBFeatureTrackerInput &frame, const vpHomogeneousMatrix &cTo) VP_OVERRIDE
void setColorUpdateRate(double updateRate)
Set the update rate for the color distribution. It should be between 0 and 1.
void loadJsonConfiguration(const nlohmann::json &) VP_OVERRIDE
void display(const vpImage< vpRGBa > &I) VP_OVERRIDE
Displays the information used for drift detection.
bool hasDiverged() const VP_OVERRIDE
Returns whether the tracking has diverged and should be reinitialized. This function should be called...
void update(const vpRBFeatureTrackerInput &previousFrame, const vpRBFeatureTrackerInput &frame, const vpHomogeneousMatrix &cTo, const vpHomogeneousMatrix &cprevTo) VP_OVERRIDE
Update the algorithm after a new tracking step.
Silhouette point simple candidate representation.
unsigned char B
Blue component.
Definition vpRGBa.h:327
unsigned char R
Red component.
Definition vpRGBa.h:325
unsigned char G
Green component.
Definition vpRGBa.h:326
float B
Blue component.
Definition vpRGBf.h:161
float G
Green component.
Definition vpRGBf.h:160
float R
Red component.
Definition vpRGBf.h:159
double getLeft() const
Definition vpRect.h:173
double getRight() const
Definition vpRect.h:179
double getBottom() const
Definition vpRect.h:97
double getTop() const
Definition vpRect.h:192
Class that consider the case of a translation vector.
vpImage< float > depth
Image containing the per-pixel normal vector (RGB, in object space).
vpHomogeneousMatrix cMo