Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpMbtMeLine.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 * Description:
31 * Make the complete tracking of an object by using its CAD model
32 */
33
34#include <visp3/core/vpConfig.h>
35#ifndef DOXYGEN_SHOULD_SKIP_THIS
36
41#include <algorithm> // (std::min)
42#include <cmath> // std::fabs
43#include <limits> // numeric_limits
44
45#include <visp3/core/vpDebug.h>
46#include <visp3/core/vpRobust.h>
47#include <visp3/core/vpTrackingException.h>
48#include <visp3/mbt/vpMbtMeLine.h>
49
51
55vpMbtMeLine::vpMbtMeLine()
56 : vpMeLine(), imin(0), imax(0), jmin(0), jmax(0), expecteddensity(0.)
57{ }
58
62vpMbtMeLine::vpMbtMeLine(const vpMbtMeLine &meline)
63 : vpMeLine(meline)
64{
65 *this = meline;
66}
67
68vpMbtMeLine &vpMbtMeLine::operator=(const vpMbtMeLine &meline)
69{
70 imin = meline.imin;
71 jmin = meline.jmin;
72 imax = meline.imax;
73 jmax = meline.jmax;
74 expecteddensity = meline.expecteddensity;
75
76 return *this;
77}
78
94void vpMbtMeLine::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ip1, const vpImagePoint &ip2,
95 double rho, double theta, bool doNoTrack)
96{
97 // 1.We do what concerns straight lines
98 // Extremities
99 double id1, jd1, id2, jd2;
100 id1 = ip1.get_i();
101 jd1 = ip1.get_j();
102 id2 = ip2.get_i();
103 jd2 = ip2.get_j();
104
105 m_PExt[0].m_ifloat = id1;
106 m_PExt[0].m_jfloat = jd1;
107 m_PExt[1].m_ifloat = id2;
108 m_PExt[1].m_jfloat = jd2;
109
110 m_rho = rho;
111 m_theta = theta;
112
113 m_a = cos(m_theta);
114 m_b = sin(m_theta);
115 m_c = -m_rho;
116
117 m_delta = atan2((jd2 - jd1), (id1 - id2));
118
119 sample(I, doNoTrack);
120
121 // 2. We call what is not specific
123
124 expecteddensity = static_cast<double>(m_meList.size());
125
126 if (!doNoTrack) {
128 }
129}
130
137unsigned int vpMbtMeLine::seekExtremities(const vpImage<unsigned char> &I)
138{
139 int nbrows = static_cast<int>(I.getHeight());
140 int nbcols = static_cast<int>(I.getWidth());
141
142 // Point extremities strictly on the straight line
143 vpImagePoint ip1, ip2;
144 getExtremities(ip1, ip2);
145 double id1 = ip1.get_i();
146 double jd1 = ip1.get_j();
147 double id2 = ip2.get_i();
148 double jd2 = ip2.get_j();
149
150 // i, j portions of the line_p
151 double diffsi = id2 - id1;
152 double diffsj = jd2 - jd1;
153 double s = sqrt(vpMath::sqr(diffsi) + vpMath::sqr(diffsj));
154
155 double sample_step = static_cast<double>(m_me->getSampleStep());
156
157 double di = diffsi * sample_step / s; // pas de risque de /0 car d(P1,P2) >0
158 double dj = diffsj * sample_step / s;
159
160 vpMeSite P;
161
162 P.init(static_cast<int>(id1), static_cast<int>(jd1), m_delta, 0, m_sign);
163 P.setDisplay(m_selectDisplay);
164 const double marginRatio = m_me->getThresholdMarginRatio();
165
166 unsigned int memory_range = m_me->getRange();
167 m_me->setRange(1);
168
169 unsigned int nb_added_points = 0;
170
171 // Try to add at max 3 points along first extremity
172 // (could be a little bit more or less)
173 for (int i = 0; i < 3; ++i) {
174 id1 -= di;
175 jd1 -= dj;
176 vpImagePoint iP;
177 iP.set_ij(id1, jd1);
178
179 if ((id1 < imin) || (id1 > imax) || (jd1 < jmin) || (jd1 > jmax)) {
180 if (vpDEBUG_ENABLE(3)) {
181 vpDisplay::displayCross(I, static_cast<int>(id1), static_cast<int>(jd1), 15, vpColor::cyan);
183 }
184 }
185 // First test to ensure that iP coordinates are > 0 before casting to unsigned int
186 else if (!outOfImage(iP, 5, nbrows, nbcols)) {
187 unsigned int is_uint = static_cast<unsigned int>(id1);
188 unsigned int js_uint = static_cast<unsigned int>(jd1);
189 if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
190 // ajout
191 P.m_ifloat = id1;
192 P.m_i = static_cast<int>(id1);
193 P.m_jfloat = jd1;
194 P.m_j = static_cast<int>(jd1);
195 double convolution = P.convolution(I, m_me);
196 double contrastThreshold = fabs(convolution) * marginRatio;
197 P.setContrastThreshold(contrastThreshold, *m_me);
198 // fin ajout
199 P.track(I, m_me, false);
201 m_meList.push_front(P);
202 nb_added_points++;
203 if (vpDEBUG_ENABLE(3)) {
205 }
206 }
207 else {
208 if (vpDEBUG_ENABLE(3)) {
210 }
211 }
212 }
213 }
214 }
215
216 // Try to add at max 3 points along second extremity
217 // (could be a little bit more or less)
218
219 for (int i = 0; i < 3; ++i) {
220 id2 += di;
221 jd2 += dj;
222
223 vpImagePoint iP;
224 iP.set_i(id2);
225 iP.set_j(jd2);
226
227 if ((id2 < imin) || (id2 > imax) || (jd2 < jmin) || (jd2 > jmax)) {
228 if (vpDEBUG_ENABLE(3)) {
229 vpDisplay::displayCross(I, static_cast<int>(id2), static_cast<int>(jd2), 5, vpColor::cyan);
231 }
232 }
233 // First test to ensure that iP coordinates are > 0 before casting to unsigned int
234 else if (!outOfImage(iP, 5, nbrows, nbcols)) {
235 unsigned int is_uint = static_cast<unsigned int>(id2);
236 unsigned int js_uint = static_cast<unsigned int>(jd2);
237 if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
238 // ajout
239 P.m_ifloat = id2;
240 P.m_i = static_cast<int>(id2);
241 P.m_jfloat = jd2;
242 P.m_j = static_cast<int>(jd2);
243 double convolution = P.convolution(I, m_me);
244 double contrastThreshold = fabs(convolution) * marginRatio;
245 P.setContrastThreshold(contrastThreshold, *m_me);
246 // fin ajout
247 P.track(I, m_me, false);
249 m_meList.push_back(P);
250 nb_added_points++;
251 if (vpDEBUG_ENABLE(3)) {
253 }
254 }
255 else {
256 if (vpDEBUG_ENABLE(3)) {
258 }
259 }
260 }
261 }
262 }
263
264 m_me->setRange(memory_range);
265 return(nb_added_points);
266}
267
273void vpMbtMeLine::suppressPoints(const vpImage<unsigned char> &I)
274{
275 for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end();) {
276 vpMeSite s = *it; // current reference pixel
277
278 // Vertical line management
279 if (fabs(sin(m_theta)) > 0.9) {
280 if ((s.m_i < imin) || (s.m_i > imax)) {
281 s.setState(vpMeSite::CONTRAST);
282 }
283 }
284 // Horizontal line management
285 else if (fabs(cos(m_theta)) > 0.9) {
286 if ((s.m_j < jmin) || (s.m_j > jmax)) {
287 s.setState(vpMeSite::CONTRAST);
288 }
289 }
290 else {
291 if ((s.m_i < imin) || (s.m_i > imax) || (s.m_j < jmin) || (s.m_j > jmax)) {
292 s.setState(vpMeSite::CONTRAST);
293 }
294 }
295 if (outOfImage(s.m_i, s.m_j, static_cast<int>(m_me->getRange() + m_me->getMaskSize() + 1), static_cast<int>(I.getHeight()), static_cast<int>(I.getWidth()))) {
296 s.setState(vpMeSite::TOO_NEAR);
297 }
298
299 if (s.getState() != vpMeSite::NO_SUPPRESSION) {
300 it = m_meList.erase(it);
301 }
302 else {
303 ++it;
304 }
305 }
306}
307
322void vpMbtMeLine::computeProjectionError(const vpImage<unsigned char> &I, double &sumErrorRad,
323 unsigned int &nbFeatures, const vpMatrix &SobelX, const vpMatrix &SobelY,
324 bool display, unsigned int length, unsigned int thickness)
325{
326 sumErrorRad = 0;
327 nbFeatures = 0;
328 double deltaNormalized = m_theta;
329 unsigned int iter = 0;
330
331 while (deltaNormalized < 0) {
332 deltaNormalized += M_PI;
333 }
334 while (deltaNormalized > M_PI) {
335 deltaNormalized -= M_PI;
336 }
337
338 vpColVector vecLine(2);
339 vecLine[0] = cos(deltaNormalized);
340 vecLine[1] = sin(deltaNormalized);
341 vecLine.normalize();
342
343 double offset = std::floor(SobelX.getRows() / 2.0);
344
345 for (std::list<vpMeSite>::const_iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
346 if (iter != 0 && iter + 1 != m_meList.size()) {
347 double gradientX = 0;
348 double gradientY = 0;
349
350 double iSite = it->m_ifloat;
351 double jSite = it->m_jfloat;
352
353 for (unsigned int i = 0; i < SobelX.getRows(); ++i) {
354 double iImg = iSite + (i - offset);
355 for (unsigned int j = 0; j < SobelX.getCols(); ++j) {
356 double jImg = jSite + (j - offset);
357
358 if (iImg < 0) {
359 iImg = 0.0;
360 }
361 if (jImg < 0) {
362 jImg = 0.0;
363 }
364
365 if (iImg > I.getHeight() - 1) {
366 iImg = I.getHeight() - 1;
367 }
368 if (jImg > I.getWidth() - 1) {
369 jImg = I.getWidth() - 1;
370 }
371
372 gradientX += SobelX[i][j] * I(static_cast<unsigned int>(iImg), static_cast<unsigned int>(jImg));
373 }
374 }
375
376 for (unsigned int i = 0; i < SobelY.getRows(); ++i) {
377 double iImg = iSite + (i - offset);
378 for (unsigned int j = 0; j < SobelY.getCols(); ++j) {
379 double jImg = jSite + (j - offset);
380
381 if (iImg < 0)
382 iImg = 0.0;
383 if (jImg < 0)
384 jImg = 0.0;
385
386 if (iImg > I.getHeight() - 1)
387 iImg = I.getHeight() - 1;
388 if (jImg > I.getWidth() - 1)
389 jImg = I.getWidth() - 1;
390
391 gradientY += SobelY[i][j] * I(static_cast<unsigned int>(iImg), static_cast<unsigned int>(jImg));
392 }
393 }
394
395 double angle = atan2(gradientX, gradientY);
396 while (angle < 0) {
397 angle += M_PI;
398 }
399 while (angle > M_PI) {
400 angle -= M_PI;
401 }
402
403 vpColVector vecGrad(2);
404 vecGrad[0] = cos(angle);
405 vecGrad[1] = sin(angle);
406 vecGrad.normalize();
407
408 double angle1 = acos(vecLine * vecGrad);
409 double angle2 = acos(vecLine * (-vecGrad));
410
411 if (display) {
412 vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(deltaNormalized)),
413 static_cast<int>(it->get_j() + length * sin(deltaNormalized)), vpColor::blue,
414 length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
415 if (angle1 < angle2) {
416 vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(angle)),
417 static_cast<int>(it->get_j() + length * sin(angle)), vpColor::red, length >= 20 ? length / 5 : 4,
418 length >= 20 ? length / 10 : 2, thickness);
419 }
420 else {
421 vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(angle + M_PI)),
422 static_cast<int>(it->get_j() + length * sin(angle + M_PI)), vpColor::red,
423 length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
424 }
425 }
426
427 sumErrorRad += std::min<double>(angle1, angle2);
428
429 nbFeatures++;
430 }
431 iter++;
432 }
433}
434
447void vpMbtMeLine::reSample(const vpImage<unsigned char> &I, const vpImagePoint &ip1, const vpImagePoint &ip2)
448{
449 m_PExt[0].m_ifloat = static_cast<double>(ip1.get_i());
450 m_PExt[0].m_jfloat = static_cast<double>(ip1.get_j());
451 m_PExt[1].m_ifloat = static_cast<double>(ip2.get_i());
452 m_PExt[1].m_jfloat = static_cast<double>(ip2.get_j());
453
455}
456
462void vpMbtMeLine::track(const vpImage<unsigned char> &I)
463{
464 if (m_mask != nullptr) {
465 // Expected density could be modified if some vpMeSite are no more tracked because they are outside the mask.
466 expecteddensity = static_cast<double>(m_meList.size());
467 }
468
470}
471
480void vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, double rho, double theta)
481{
482 m_rho = rho;
483 m_theta = theta;
484 m_a = cos(m_theta);
485 m_b = sin(m_theta);
486 m_c = -m_rho;
487 // Search for points at the ends of the straight line in the case of sliding
488 suppressPoints(I);
489 seekExtremities(I);
490 suppressPoints(I);
491 // Resampling if necessary
493 // Updates the delta angle for each point in the list
494 updateDelta();
495}
496
507void vpMbtMeLine::updateParameters(const vpImage<unsigned char> &I, const vpImagePoint &ip1, const vpImagePoint &ip2,
508 double rho, double theta)
509{
510 m_rho = rho;
511 m_theta = theta;
512 m_a = cos(m_theta);
513 m_b = sin(m_theta);
514 m_c = -m_rho;
515 // Search for points at the ends of the straight line in the case of sliding
516 suppressPoints(I);
517 seekExtremities(I);
518 suppressPoints(I);
519 // Resampling if necessary
520 reSample(I, ip1, ip2);
521
522 // Updates the delta angle for each point in the list
523 updateDelta();
524}
525
526END_VISP_NAMESPACE
527#endif
unsigned int getCols() const
Definition vpArray2D.h:423
unsigned int getRows() const
Definition vpArray2D.h:433
Implementation of column vector and the associated operations.
static const vpColor red
Definition vpColor.h:198
static const vpColor cyan
Definition vpColor.h:207
static const vpColor blue
Definition vpColor.h:204
static const vpColor green
Definition vpColor.h:201
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
static void flush(const vpImage< unsigned char > &I)
static void displayArrow(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color=vpColor::white, unsigned int w=4, unsigned int h=2, unsigned int thickness=1)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
void set_j(double jj)
double get_j() const
void set_ij(double ii, double jj)
void set_i(double ii)
double get_i() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
static double sqr(double x)
Definition vpMath.h:203
Implementation of a matrix and operations on matrices.
Definition vpMatrix.h:175
Class that tracks in an image a line moving edges.
Definition vpMeLine.h:157
void reSample(const vpImage< unsigned char > &I)
Definition vpMeLine.cpp:561
void track(const vpImage< unsigned char > &I)
Definition vpMeLine.cpp:607
Performs search in a given direction(normal) for a given distance(pixels) for a given 'site'....
Definition vpMeSite.h:75
@ TOO_NEAR
Point not tracked anymore, since too near from its neighbor.
Definition vpMeSite.h:100
@ CONTRAST
Point not tracked due to a contrast problem, but retained in the ME list.
Definition vpMeSite.h:94
@ NO_SUPPRESSION
Point successfully tracked.
Definition vpMeSite.h:93
void setDisplay(vpMeSiteDisplayType select)
Definition vpMeSite.h:287
double m_ifloat
Subpixel coordinates along i of a site.
Definition vpMeSite.h:110
double convolution(const vpImage< unsigned char > &ima, const vpMe *me)
Definition vpMeSite.cpp:259
void init()
Definition vpMeSite.cpp:70
vpMeSiteState getState() const
Definition vpMeSite.h:306
int m_j
Integer coordinates along j of a site.
Definition vpMeSite.h:108
int m_i
Integer coordinate along i of a site.
Definition vpMeSite.h:106
double m_jfloat
Subpixel coordinates along j of a site.
Definition vpMeSite.h:112
void setContrastThreshold(const double &thresh, const vpMe &me)
Definition vpMeSite.h:326
void track(const vpImage< unsigned char > &I, const vpMe *me, const bool &test_contrast=true)
Definition vpMeSite.cpp:330
void initTracking(const vpImage< unsigned char > &I)
void track(const vpImage< unsigned char > &I)
#define vpDEBUG_ENABLE(level)
Definition vpDebug.h:585