Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpRBKltTracker.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/vpRBKltTracker.h>
32
33#if defined(VP_HAVE_RB_KLT_TRACKER)
34
35#include <visp3/core/vpImageConvert.h>
36#include <visp3/core/vpPixelMeterConversion.h>
37#include <visp3/core/vpMeterPixelConversion.h>
38#include <visp3/core/vpDisplay.h>
39
41
42inline bool isTooCloseToBorder(unsigned int i, unsigned int j, unsigned int h, unsigned w, unsigned int border)
43{
44 return i < border || j < border || i >(h - border) || j >(w - border);
45}
46
47inline void vpRBKltTracker::tryAddNewPoint(
48 const vpRBFeatureTrackerInput &frame,
49 std::map<long, vpRBKltTracker::vpTrackedKltPoint> &points,
50 long id, const float u, const float v,
51 const vpHomogeneousMatrix &cMo, const vpHomogeneousMatrix &oMc)
52{
53 unsigned int uu = static_cast<unsigned int>(round(u)), uv = static_cast<unsigned int>(round(v));
54 if (isTooCloseToBorder(uv, uu, frame.renders.depth.getRows(), frame.renders.depth.getCols(), m_border)) {
55 return;
56 }
57
58 float Z = frame.renders.depth[uv][uu];
59 if (Z <= 0.f || (frame.hasDepth() && frame.depth[uv][uu] > 0.f && fabs(frame.depth[uv][uu] - Z) > 5e-3)) {
60 return;
61 }
62 vpRBKltTracker::vpTrackedKltPoint p;
63 p.cTo0 = cMo;
64 vpRGBf normalRGB = frame.renders.normals[uv][uu];
65 p.normal = vpColVector({ normalRGB.R, normalRGB.G, normalRGB.B });
66 double x = 0.0, y = 0.0;
67 vpPixelMeterConversion::convertPoint(frame.cam, static_cast<double>(u), static_cast<double>(v), x, y);
68 vpColVector oC({ x * Z, y * Z, Z, 1.0 });
69 vpColVector oX = oMc * oC;
70 oX /= oX[3];
71 p.oX = vpPoint(oX[0], oX[1], oX[2]);
72 p.currentPos = vpImagePoint(y, x);
73 points[id] = p;
74
75}
76
78 vpRBFeatureTracker(), m_numPointsReinit(20), m_newPointsDistanceThreshold(5.0), m_border(5),
79 m_maxErrorOutliersPixels(10.0), m_useMask(false), m_minMaskConfidence(0.0)
80{ }
81
82void vpRBKltTracker::extractFeatures(const vpRBFeatureTrackerInput &frame, const vpRBFeatureTrackerInput & /*previousFrame*/, const vpHomogeneousMatrix &/*cMo*/)
83{
84 m_Iprev = m_I;
85 vpImageConvert::convert(frame.I, m_I);
86 const vpHomogeneousMatrix &cMo = frame.renders.cMo;
87 const vpHomogeneousMatrix oMc = cMo.inverse();
88 if (m_maxErrorOutliersPixels > 0.0) {
89 const double distanceThresholdPxSquare = vpMath::sqr(m_maxErrorOutliersPixels);
90 const unsigned int nbFeatures = static_cast<unsigned int>(m_klt.getNbFeatures());
91 std::vector<unsigned> kltIndicesToRemove;
92 // Detect outliers
93 for (unsigned int i = 0; i < nbFeatures; ++i) {
94 long id = 0;
95 float u = 0.f, v = 0.f;
96 m_klt.getFeature(i, id, u, v);
97 if (m_points.find(id) == m_points.end()) {
98 continue;
99 }
100 unsigned int uu = static_cast<unsigned int>(round(u)), uv = static_cast<unsigned int>(round(v));
101 if (isTooCloseToBorder(uv, uu, frame.renders.depth.getRows(), frame.renders.depth.getCols(), m_border)) {
102 continue;
103 }
104 const float Z = frame.renders.depth[uv][uu];
105 if (Z <= 0.f) {
106 continue;
107 }
108
109 vpTrackedKltPoint &p = m_points[id];
110 double x = 0.0, y = 0.0;
111 vpPixelMeterConversion::convertPoint(frame.cam, static_cast<double>(u), static_cast<double>(v), x, y);
112 vpColVector oXn = oMc * vpColVector({ x * Z, y * Z, Z, 1.0 });
113 oXn /= oXn[3];
114 p.update(cMo);
115 double x1 = p.oX.get_x(), y1 = p.oX.get_y();
116 double u1 = 0.0, v1 = 0.0;
117 vpMeterPixelConversion::convertPoint(frame.cam, x1, y1, u1, v1);
118 double distancePx = vpMath::sqr(u1 - u) + vpMath::sqr(v1 - v);
119
120 vpColVector oX = p.oX.get_oP();
121 if (distancePx > distanceThresholdPxSquare) {
122 m_points.erase(id);
123 kltIndicesToRemove.push_back(i);
124 }
125 }
126 // Remove tracking from klt: iterate in reverse order to invalidate iterator i (shifting in the klt list)
127 for (int i = static_cast<int>(kltIndicesToRemove.size()) - 1; i >= 0; --i) {
128 m_klt.suppressFeature(kltIndicesToRemove[i]);
129 }
130 }
131
132 cv::Mat mask = cv::Mat::zeros(m_I.rows, m_I.cols, CV_8U);
133 const vpRect bb = frame.renders.boundingBox;
134 for (unsigned int i = static_cast<unsigned int>(bb.getTop()); i < static_cast<unsigned int>(bb.getBottom()); ++i) {
135 for (unsigned int j = static_cast<unsigned int>(bb.getLeft()); j < static_cast<unsigned int>(bb.getRight()); ++j) {
136 mask.at<unsigned char>(i, j) = (frame.renders.depth[i][j] > 0.f) * 255;
137 }
138 }
139
140 cv::Rect roi(static_cast<int>(bb.getLeft()), static_cast<int>(bb.getTop()), static_cast<int>(bb.getWidth()), static_cast<int>(bb.getHeight()));
141 cv::Mat maskRoi = mask(roi);
142
143 if (m_Iprev.rows > 0) {
144 // Consider that there are not enough points: reinit KLT tracking
145 if (m_points.size() < m_numPointsReinit) {
146 cv::Mat IprevRoi = m_Iprev(roi);
147 m_klt.initTracking(m_Iprev, mask);
148 const unsigned int nbFeatures = static_cast<unsigned int>(m_klt.getNbFeatures());
149 m_points.clear();
150 for (unsigned int i = 0; i < nbFeatures; ++i) {
151 long id;
152 float u, v;
153 m_klt.getFeature(i, id, u, v);
154 tryAddNewPoint(frame, m_points, id, u, v, cMo, oMc);
155 }
156 }
157 else { // Otherwise, try and get new points
158 vpKltOpencv kltTemp;
159 kltTemp.setMaxFeatures(m_klt.getMaxFeatures());
160 kltTemp.setWindowSize(m_klt.getWindowSize());
161 kltTemp.setQuality(m_klt.getQuality());
162 kltTemp.setMinDistance(m_klt.getMinDistance());
163 kltTemp.setHarrisFreeParameter(m_klt.getHarrisFreeParameter());
164 kltTemp.setBlockSize(m_klt.getBlockSize());
165 kltTemp.setPyramidLevels(m_klt.getPyramidLevels());
166 kltTemp.initTracking(m_Iprev(roi), maskRoi);
167 const unsigned int nbFeaturesTemp = static_cast<unsigned int>(kltTemp.getNbFeatures());
168 const unsigned int nbFeatures = static_cast<unsigned int>(m_klt.getNbFeatures());
169 for (unsigned int i = 0; i < nbFeaturesTemp; ++i) {
170 double threshold = vpMath::sqr(m_newPointsDistanceThreshold); // distance threshold, in squared pixels
171 double tooClose = false;
172 float u, v;
173 long id;
174 kltTemp.getFeature(i, id, u, v);
175 // Realign features from bounding box coordinates to image coordinates
176 u += static_cast<float>(bb.getLeft());
177 v += static_cast<float>(bb.getTop());
178 for (unsigned int j = 0; j < nbFeatures; ++j) {
179 float uj, vj;
180 long idj;
181 m_klt.getFeature(j, idj, uj, vj);
182 if (vpMath::sqr(uj - u) + vpMath::sqr(vj - v) < threshold) {
183 tooClose = true;
184 break;
185 }
186 }
187 if (tooClose) {
188 continue;
189 }
190
191 m_klt.addFeature(u, v);
192 const std::vector<long> &ids = m_klt.getFeaturesId();
193 id = ids[ids.size() - 1];
194 tryAddNewPoint(frame, m_points, id, u, v, cMo, oMc);
195 }
196 }
197 }
198 else {
199 m_klt.initTracking(m_I, mask);
200 m_points.clear();
201 const unsigned int nbFeatures = static_cast<unsigned int>(m_klt.getNbFeatures());
202 for (unsigned int i = 0; i < nbFeatures; ++i) {
203 long id;
204 float u, v;
205 m_klt.getFeature(i, id, u, v);
206 tryAddNewPoint(frame, m_points, id, u, v, cMo, oMc);
207 }
208 }
209}
210
211void vpRBKltTracker::trackFeatures(const vpRBFeatureTrackerInput &frame, const vpRBFeatureTrackerInput &/*previousFrame*/, const vpHomogeneousMatrix &cMo)
212{
213 unsigned int nbKltFeatures = static_cast<unsigned int>(m_klt.getNbFeatures());
214 if (nbKltFeatures > 0) {
215 m_klt.track(m_I);
216 }
217 std::map<long, vpTrackedKltPoint> newPoints;
218 const vpHomogeneousMatrix oMc = cMo.inverse();
219
220 bool testMask = m_useMask && frame.hasMask();
221 nbKltFeatures = static_cast<unsigned int>(m_klt.getNbFeatures());
222 std::vector<unsigned> kltIndicesToRemove;
223 for (unsigned int i = 0; i < nbKltFeatures; ++i) {
224 long id = 0;
225 float u = 0.f, v = 0.f;
226 double x = 0.0, y = 0.0;
227 m_klt.getFeature(i, id, u, v);
228 unsigned int uu = static_cast<unsigned int>(round(u)), uv = static_cast<unsigned int>(round(v));
229 // Filter points that are too close to image borders and cannot be reliably tracked
230 if (isTooCloseToBorder(uv, uu, frame.renders.depth.getRows(), frame.renders.depth.getCols(), m_border)) {
231 continue;
232 }
233 float Z = frame.renders.depth[uv][uu];
234 if (Z <= 0.f) {
235 continue;
236 }
237
238 if (testMask && frame.mask[uv][uu] < m_minMaskConfidence) {
239 continue;
240 }
241
242 if (m_points.find(id) != m_points.end()) {
243 vpTrackedKltPoint &p = m_points[id];
244 if (p.rotationDifferenceToInitial(oMc) > vpMath::rad(45.0) && p.normalDotProd(cMo) < cos(vpMath::rad(70))) {
245 continue;
246 }
247 vpPixelMeterConversion::convertPoint(frame.cam, static_cast<double>(u), static_cast<double>(v), x, y);
248 p.currentPos = vpImagePoint(y, x);
249 newPoints[id] = p;
250 }
251 else {
252 kltIndicesToRemove.push_back(i);
253 }
254 }
255
256 // Remove tracking from klt: iterate in reverse order to invalidate iterator i (shifting in the klt list)
257 for (int i = static_cast<int>(kltIndicesToRemove.size()) - 1; i >= 0; --i) {
258 m_klt.suppressFeature(kltIndicesToRemove[i]);
259 }
260
261 m_points = newPoints;
262 m_numFeatures = static_cast<unsigned int>(m_points.size()) * 2;
263}
264
265void vpRBKltTracker::initVVS(const vpRBFeatureTrackerInput &/*frame*/, const vpRBFeatureTrackerInput &/*previousFrame*/, const vpHomogeneousMatrix & /*cMo*/)
266{
267 if (m_numFeatures < m_numPointsReinit * 2) {
268 m_numFeatures = 0;
269 return;
270 }
271 m_L.resize(m_numFeatures, 6, false, false);
272 m_error.resize(m_numFeatures, false);
273 m_weighted_error.resize(m_numFeatures, false);
274 m_weights.resize(m_numFeatures, false);
275 m_LTL.resize(6, 6, false, false);
276 m_LTR.resize(6, false);
277 m_cov.resize(6, 6, false, false);
278 m_covWeightDiag.resize(m_numFeatures, false);
279 m_error = 0;
280}
281
282void vpRBKltTracker::computeVVSIter(const vpRBFeatureTrackerInput &/*frame*/, const vpHomogeneousMatrix &cMo, unsigned int /*iteration*/)
283{
284 if (m_numFeatures < m_numPointsReinit * 2) {
285 m_LTL = 0;
286 m_LTR = 0;
287 m_error = 0;
288 return;
289 }
290 unsigned int pointIndex = 0;
291
292 for (std::pair<const long, vpTrackedKltPoint> &p : m_points) {
293 p.second.update(cMo);
294 p.second.interaction(m_L, pointIndex);
295 p.second.error(m_error, pointIndex);
296 ++pointIndex;
297 }
298
299 //m_robust.setMinMedianAbsoluteDeviation(2.0 / frame.cam.get_px());
300 m_robust.MEstimator(vpRobust::TUKEY, m_error, m_weights);
301
303}
304
306 const vpImage<vpRGBa> &/*IRGB*/, const vpImage<unsigned char> &/*depth*/) const
307{
308 for (const std::pair<const long, vpTrackedKltPoint> &p : m_points) {
309 double u = 0.0, v = 0.0;
310 vpMeterPixelConversion::convertPoint(cam, p.second.currentPos.get_j(), p.second.currentPos.get_i(), u, v);
311 vpDisplay::displayPoint(I, static_cast<int>(v), static_cast<int>(u), vpColor::red, 2);
312 }
313}
314
315#endif
316
317END_VISP_NAMESPACE
Generic class defining intrinsic camera parameters.
static const vpColor red
Definition vpColor.h:198
static void displayPoint(const vpImage< unsigned char > &I, const vpImagePoint &ip, const vpColor &color, unsigned int thickness=1)
Implementation of an homogeneous matrix and operations on such kind of matrices.
static void convert(const vpImage< unsigned char > &src, vpImage< vpRGBa > &dest)
Definition of the vpImage class member functions.
Definition vpImage.h:131
unsigned int getCols() const
Definition vpImage.h:171
unsigned int getRows() const
Definition vpImage.h:212
void setBlockSize(int blockSize)
void setQuality(double qualityLevel)
int getNbFeatures() const
Get the number of current features.
void setHarrisFreeParameter(double harris_k)
void getFeature(const int &index, long &id, float &x, float &y) const
void setMaxFeatures(int maxCount)
void initTracking(const cv::Mat &I, const cv::Mat &mask=cv::Mat())
void setMinDistance(double minDistance)
void setWindowSize(int winSize)
void setPyramidLevels(int pyrMaxLevel)
static double rad(double deg)
Definition vpMath.h:129
static double sqr(double x)
Definition vpMath.h:203
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< unsigned char > I
vpImage< float > mask
depth image, 0 sized if depth is not available
vpImage< float > depth
RGB image, 0 sized if RGB is not available.
vpRBRenderData renders
camera parameters
A base class for all features that can be used and tracked in the vpRBTracker.
void updateOptimizerTerms(const vpHomogeneousMatrix &cMo)
vpColVector m_weights
Weighted VS error.
vpMatrix m_LTL
Error jacobian (In VS terms, the interaction matrix).
vpColVector m_covWeightDiag
Covariance matrix.
vpColVector m_LTR
Left side of the Gauss newton minimization.
unsigned m_numFeatures
Error weights.
vpColVector m_weighted_error
Raw VS Error vector.
vpMatrix m_cov
Right side of the Gauss Newton minimization.
void extractFeatures(const vpRBFeatureTrackerInput &frame, const vpRBFeatureTrackerInput &previousFrame, const vpHomogeneousMatrix &cMo) VP_OVERRIDE
Extract features from the frame data and the current pose estimate.
void trackFeatures(const vpRBFeatureTrackerInput &frame, const vpRBFeatureTrackerInput &previousFrame, const vpHomogeneousMatrix &cMo) VP_OVERRIDE
Track the features.
void display(const vpCameraParameters &cam, const vpImage< unsigned char > &I, const vpImage< vpRGBa > &IRGB, const vpImage< unsigned char > &depth) const VP_OVERRIDE
void initVVS(const vpRBFeatureTrackerInput &frame, const vpRBFeatureTrackerInput &previousFrame, const vpHomogeneousMatrix &cMo) VP_OVERRIDE
void computeVVSIter(const vpRBFeatureTrackerInput &frame, const vpHomogeneousMatrix &cMo, unsigned int iteration) VP_OVERRIDE
float B
Blue component.
Definition vpRGBf.h:161
float G
Green component.
Definition vpRGBf.h:160
float R
Red component.
Definition vpRGBf.h:159
@ TUKEY
Tukey influence function.
Definition vpRobust.h:89
vpImage< float > depth
Image containing the per-pixel normal vector (RGB, in object space).
vpHomogeneousMatrix cMo
vpImage< vpRGBf > normals