Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpMbtMeEllipse.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 * Moving edges.
32 */
33
34#ifndef DOXYGEN_SHOULD_SKIP_THIS
35
36#include <visp3/mbt/vpMbtMeEllipse.h>
37
38#include <visp3/core/vpDebug.h>
39#include <visp3/core/vpImagePoint.h>
40#include <visp3/core/vpRobust.h>
41#include <visp3/core/vpTrackingException.h>
42#include <visp3/me/vpMe.h>
43
44#include <algorithm> // (std::min)
45#include <cmath> // std::fabs
46#include <limits> // numeric_limits
47
49
65void vpMbtMeEllipse::computeProjectionError(const vpImage<unsigned char> &I, double &sumErrorRad,
66 unsigned int &nbFeatures, const vpMatrix &SobelX, const vpMatrix &SobelY,
67 bool display, unsigned int length, unsigned int thickness)
68{
69 sumErrorRad = 0;
70 nbFeatures = 0;
71
72 double offset = static_cast<double>(std::floor(static_cast<double>(SobelX.getRows()) / 2.0));
73 int height = static_cast<int>(I.getHeight());
74 int width = static_cast<int>(I.getWidth());
75
76 double max_iImg = height - 1.;
77 double max_jImg = width - 1.;
78
79 vpColVector vecSite(2);
80 vpColVector vecGrad(2);
81
82 for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
83 double iSite = it->m_ifloat;
84 double jSite = it->m_jfloat;
85
86 if (!outOfImage(vpMath::round(iSite), vpMath::round(jSite), 0, height, width)) { // Check if necessary
87 // The tangent angle to the ellipse at a site
88 double theta = computeTheta(vpImagePoint(iSite, jSite));
89
90 vecSite[0] = cos(theta);
91 vecSite[1] = sin(theta);
92 vecSite.normalize();
93
94 double gradientX = 0;
95 double gradientY = 0;
96
97 for (unsigned int i = 0; i < SobelX.getRows(); i++) {
98 double iImg = iSite + (i - offset);
99 for (unsigned int j = 0; j < SobelX.getCols(); j++) {
100 double jImg = jSite + (j - offset);
101
102 if (iImg < 0)
103 iImg = 0.0;
104 if (jImg < 0)
105 jImg = 0.0;
106
107 if (iImg > max_iImg)
108 iImg = max_iImg;
109 if (jImg > max_jImg)
110 jImg = max_jImg;
111
112 gradientX += SobelX[i][j] * I(static_cast<unsigned int>(iImg), static_cast<unsigned int>(jImg));
113 }
114 }
115
116 for (unsigned int i = 0; i < SobelY.getRows(); i++) {
117 double iImg = iSite + (i - offset);
118 for (unsigned int j = 0; j < SobelY.getCols(); j++) {
119 double jImg = jSite + (j - offset);
120
121 if (iImg < 0)
122 iImg = 0.0;
123 if (jImg < 0)
124 jImg = 0.0;
125
126 if (iImg > max_iImg)
127 iImg = max_iImg;
128 if (jImg > max_jImg)
129 jImg = max_jImg;
130
131 gradientY += SobelY[i][j] * I(static_cast<unsigned int>(iImg), static_cast<unsigned int>(jImg));
132 }
133 }
134
135 double angle = atan2(gradientY, gradientX);
136 while (angle < 0)
137 angle += M_PI;
138 while (angle > M_PI)
139 angle -= M_PI;
140
141 vecGrad[0] = cos(angle);
142 vecGrad[1] = sin(angle);
143 vecGrad.normalize();
144
145 double angle1 = acos(vecSite * vecGrad);
146 double angle2 = acos(vecSite * (-vecGrad));
147
148 if (display) {
149 vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(theta)),
150 static_cast<int>(it->get_j() + length * sin(theta)), vpColor::blue,
151 length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
152 if (angle1 < angle2) {
153 vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(angle)),
154 static_cast<int>(it->get_j() + length * sin(angle)), vpColor::red,
155 length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
156 }
157 else {
158 vpDisplay::displayArrow(I, it->get_i(), it->get_j(),
159 static_cast<int>(it->get_i() + length * cos(angle + M_PI)),
160 static_cast<int>(it->get_j() + length * sin(angle + M_PI)), vpColor::red,
161 length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
162 }
163 }
164
165 sumErrorRad += std::min<double>(angle1, angle2);
166
167 nbFeatures++;
168 }
169 }
170}
171
172void vpMbtMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &center_p, double n20_p,
173 double n11_p, double n02_p, bool doNotTrack, vpImagePoint *pt1,
174 const vpImagePoint *pt2)
175{
176 if (pt1 != nullptr && pt2 != nullptr) {
177 m_trackArc = true;
178 }
179
180 // useful for sample(I) : uc, vc, a, b, e, Ki, alpha1, alpha2
181 m_uc = center_p.get_u();
182 m_vc = center_p.get_v();
183 m_n20 = n20_p;
184 m_n11 = n11_p;
185 m_n02 = n02_p;
186
187 computeAbeFromNij();
188 computeKiFromNij();
189
190 if (m_trackArc) {
191 m_alpha1 = computeAngleOnEllipse(*pt1);
192 m_alpha2 = computeAngleOnEllipse(*pt2);
193 if ((m_alpha2 <= m_alpha1) || (std::fabs(m_alpha2 - m_alpha1) < m_arcEpsilon)) {
194 m_alpha2 += 2.0 * M_PI;
195 }
196 // useful for track(I)
197 m_iP1 = *pt1;
198 m_iP2 = *pt2;
199 }
200 else {
201 m_alpha1 = 0.0;
202 m_alpha2 = 2.0 * M_PI;
203 // useful for track(I)
204 vpImagePoint ip;
205 computePointOnEllipse(m_alpha1, ip);
206 m_iP1 = ip;
207 m_iP2 = ip;
208 }
209 // useful for display(I) so useless if no display before track(I)
210 m_iPc.set_uv(m_uc, m_vc);
211
212 sample(I, doNotTrack);
213
214 try {
215 if (!doNotTrack)
216 track(I);
217 }
218 catch (const vpException &exception) {
219 throw(exception);
220 }
221}
222
228void vpMbtMeEllipse::track(const vpImage<unsigned char> &I)
229{
230 try {
232 if (m_mask != nullptr) {
233 // Expected density could be modified if some vpMeSite are no more tracked because they are outside the mask.
234 m_expectedDensity = static_cast<unsigned int>(m_meList.size());
235 }
236 }
237 catch (const vpException &exception) {
238 throw(exception);
239 }
240}
241
245void vpMbtMeEllipse::updateParameters(const vpImage<unsigned char> &I, const vpImagePoint &center_p, double n20_p,
246 double n11_p, double n02_p)
247{
248 m_uc = center_p.get_u();
249 m_vc = center_p.get_v();
250 m_n20 = n20_p;
251 m_n11 = n11_p;
252 m_n02 = n02_p;
253
254 computeAbeFromNij();
255 computeKiFromNij();
256
257 suppressPoints();
258 reSample(I);
259
260 // remet a jour l'angle delta pour chaque point de la liste
261 updateTheta();
262}
263
277void vpMbtMeEllipse::reSample(const vpImage<unsigned char> &I)
278{
279 if (!m_me) {
280 vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
281 throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
282 }
283
284 unsigned int n = numberOfSignal();
285 if (static_cast<double>(n) < 0.9 * m_expectedDensity) {
286 sample(I);
288 }
289}
290
303void vpMbtMeEllipse::sample(const vpImage<unsigned char> &I, bool doNotTrack)
304{
305 // Warning: similar code in vpMeEllipse::sample() except for display that is removed here
306 if (!m_me) {
307 throw(vpException(vpException::fatalError, "Moving edges on ellipse not initialized"));
308 }
309 // Delete old lists
310 m_meList.clear();
311 m_angleList.clear();
312
313 int nbrows = static_cast<int>(I.getHeight());
314 int nbcols = static_cast<int>(I.getWidth());
315
316 if (std::fabs(m_me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
317 std::cout << "In vpMeEllipse::sample: ";
318 std::cout << "function called with sample step = 0, set to 10 dg";
319 m_me->setSampleStep(10.0);
320 }
321 double incr = vpMath::rad(m_me->getSampleStep()); // angle increment
322 // alpha2 - alpha1 = 2 * M_PI for a complete ellipse
323 m_expectedDensity = static_cast<unsigned int>(floor((m_alpha2 - m_alpha1) / incr));
324
325 // starting angle for sampling
326 double ang = m_alpha1 + ((m_alpha2 - m_alpha1) - static_cast<double>(m_expectedDensity) * incr) / 2.0;
327 // sample positions
328 for (unsigned int i = 0; i < m_expectedDensity; i++) {
329 vpImagePoint iP;
330 computePointOnEllipse(ang, iP);
331 // If point is in the image, add to the sample list
332 // Check done in (i,j) frame)
333 if (!outOfImage(vpMath::round(iP.get_i()), vpMath::round(iP.get_j()), 0, nbrows, nbcols)) {
334 double theta = computeTheta(iP);
335 vpMeSite pix;
336 // (i,j) frame used for vpMeSite
337 pix.init(iP.get_i(), iP.get_j(), theta);
338 pix.setDisplay(m_selectDisplay);
340 const double marginRatio = m_me->getThresholdMarginRatio();
341 double convolution = pix.convolution(I, m_me);
342 double contrastThreshold = fabs(convolution) * marginRatio;
343 pix.setContrastThreshold(contrastThreshold, *m_me);
344 m_meList.push_back(pix);
345 m_angleList.push_back(ang);
346 }
347 ang += incr;
348 }
349 if (!doNotTrack) {
351 }
352}
353
358void vpMbtMeEllipse::suppressPoints()
359{
360 // Loop through list of sites to track
361 for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end();) {
362 vpMeSite s = *it; // current reference pixel
363 if (s.getState() != vpMeSite::NO_SUPPRESSION)
364 it = m_meList.erase(it);
365 else
366 ++it;
367 }
368}
369END_VISP_NAMESPACE
370#endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS
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 blue
Definition vpColor.h:204
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)
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ fatalError
Fatal error.
Definition vpException.h:72
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
double get_j() const
double get_u() const
double get_i() const
double get_v() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
static double rad(double deg)
Definition vpMath.h:129
static int round(double x)
Definition vpMath.h:413
Implementation of a matrix and operations on matrices.
Definition vpMatrix.h:175
void track(const vpImage< unsigned char > &I)
Performs search in a given direction(normal) for a given distance(pixels) for a given 'site'....
Definition vpMeSite.h:75
@ NO_SUPPRESSION
Point successfully tracked.
Definition vpMeSite.h:93
void setDisplay(vpMeSiteDisplayType select)
Definition vpMeSite.h:287
double convolution(const vpImage< unsigned char > &ima, const vpMe *me)
Definition vpMeSite.cpp:259
void init()
Definition vpMeSite.cpp:70
void setContrastThreshold(const double &thresh, const vpMe &me)
Definition vpMeSite.h:326
void setState(const vpMeSiteState &flag)
Definition vpMeSite.h:296
void initTracking(const vpImage< unsigned char > &I)
Error that can be emitted by the vpTracker class and its derivatives.
@ initializationError
Tracker initialization error.
#define vpDERROR_TRACE
Definition vpDebug.h:499