1 /****************************************************************************
2 *
3 * ViSP, open source Visual Servoing Platform software.
4 * Copyright (C) 2005 - 2019 by Inria. All rights reserved.
5 *
6 * This software is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 * See the file LICENSE.txt at the root directory of this source
11 * distribution for additional information about the GNU GPL.
12 *
13 * For using ViSP with software that can not be combined with the GNU
14 * GPL, please contact Inria about acquiring a ViSP Professional
15 * Edition License.
16 *
17 * See http://visp.inria.fr for more information.
18 *
19 * This software was developed at:
20 * Inria Rennes - Bretagne Atlantique
21 * Campus Universitaire de Beaulieu
22 * 35042 Rennes Cedex
23 * France
24 *
25 * If you have questions regarding the use of this file, please contact
26 * Inria at visp@inria.fr
27 *
28 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30 *
31 * Description:
32 * Moving edges.
33 *
34 * Authors:
35 * Eric Marchand
36 *
37 *****************************************************************************/
38
39 #include <visp3/me/vpMeEllipse.h>
40
41 #include <visp3/core/vpDebug.h>
42 #include <visp3/core/vpException.h>
43 #include <visp3/core/vpImagePoint.h>
44 #include <visp3/core/vpRobust.h>
45 #include <visp3/me/vpMe.h>
46
47 #include <cmath> // std::fabs
48 #include <limits> // numeric_limits
49 #include <vector>
50
51 /*!
52 Basic constructor that calls the constructor of the class vpMeTracker.
53 */
vpMeEllipse()54 vpMeEllipse::vpMeEllipse()
55 : K(), iPc(), a(0.), b(0.), e(0.),
56 iP1(), iP2(), alpha1(0), alpha2(2 * M_PI),
57 ce(0.), se(0.), angle(), m00(0.),
58 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
59 mu11(0.), mu20(0.), mu02(0.),
60 m10(0.), m01(0.),
61 m11(0.), m02(0.), m20(0.),
62 #endif
63 thresholdWeight(0.2),
64 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
65 expecteddensity(0.),
66 #endif
67 m_alphamin(0.), m_alphamax(0.), m_uc(0.), m_vc(0.), m_n20(0.), m_n11(0.), m_n02(0.),
68 m_expectedDensity(0), m_numberOfGoodPoints(0), m_trackArc(false), m_arcEpsilon(1e-6)
69 {
70 // Resize internal parameters vector
71 // K0 u^2 + K1 v^2 + 2 K2 u v + 2 K3 u + 2 K4 v + K5 = 0
72 K.resize(6);
73 iP1.set_ij(0,0);
74 iP2.set_ij(0,0);
75 }
76
77 /*!
78 Copy constructor.
79 */
vpMeEllipse(const vpMeEllipse & me_ellipse)80 vpMeEllipse::vpMeEllipse(const vpMeEllipse &me_ellipse)
81 : vpMeTracker(me_ellipse),
82 K(me_ellipse.K), iPc(me_ellipse.iPc), a(me_ellipse.a), b(me_ellipse.b), e(me_ellipse.e),
83 iP1(me_ellipse.iP1), iP2(me_ellipse.iP2), alpha1(me_ellipse.alpha1), alpha2(me_ellipse.alpha2),
84 ce(me_ellipse.ce), se(me_ellipse.se), angle(me_ellipse.angle), m00(me_ellipse.m00),
85 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
86 mu11(me_ellipse.mu11), mu20(me_ellipse.mu20), mu02(me_ellipse.mu02),
87 m10(me_ellipse.m10), m01(me_ellipse.m01), m11(me_ellipse.m11),
88 m02(me_ellipse.m02), m20(me_ellipse.m20),
89 #endif
90 thresholdWeight(me_ellipse.thresholdWeight),
91 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
92 expecteddensity(me_ellipse.expecteddensity),
93 #endif
94 m_alphamin(me_ellipse.m_alphamin), m_alphamax(me_ellipse.m_alphamax),
95 m_uc(me_ellipse.m_uc), m_vc(me_ellipse.m_vc),
96 m_n20(me_ellipse.m_n20), m_n11(me_ellipse.m_n11), m_n02(me_ellipse.m_n02),
97 m_expectedDensity(me_ellipse.m_expectedDensity), m_numberOfGoodPoints(me_ellipse.m_numberOfGoodPoints), m_trackArc(me_ellipse.m_trackArc)
98 {
99 }
100
101 /*!
102 Basic destructor.
103 */
~vpMeEllipse()104 vpMeEllipse::~vpMeEllipse()
105 {
106 list.clear();
107 angle.clear();
108 }
109
110 /*!
111 Computes the \f$ \theta \f$ angle that represents the angle between the
112 tangent to the curve and the u axis. This angle is used for tracking the
113 vpMeSite.
114
115 \param iP : The point belonging to the ellipse where the angle is computed.
116 */
computeTheta(const vpImagePoint & iP) const117 double vpMeEllipse::computeTheta(const vpImagePoint &iP) const
118 {
119 double u = iP.get_u();
120 double v = iP.get_v();
121
122 return (computeTheta(u, v));
123 }
124
125 /*!
126 Computes the \f$ \theta \f$ angle that represents the angle between the
127 tangent to the curve and the u axis. This angle is used for tracking the
128 vpMeSite.
129
130 \param u,v : The point belonging to the ellipse where the angle is computed.
131 */
computeTheta(double u,double v) const132 double vpMeEllipse::computeTheta(double u, double v) const
133 {
134 double A = K[0] * u + K[2] * v + K[3];
135 double B = K[1] * v + K[2] * u + K[4];
136
137 double theta = atan2(B, A); // Angle between the tangent and the u axis.
138 if (theta < 0) { // theta in [0;Pi] // FC : pourquoi ? pour me sans doute
139 theta += M_PI;
140 }
141 return (theta);
142 }
143
144 /*!
145 Compute the \f$ theta \f$ angle for each vpMeSite.
146
147 \note The \f$ theta \f$ angle is useful during the tracking part.
148 */
updateTheta()149 void vpMeEllipse::updateTheta()
150 {
151 vpMeSite p_me;
152 vpImagePoint iP;
153 for (std::list<vpMeSite>::iterator it = list.begin(); it != list.end(); ++it) {
154 p_me = *it;
155 // (i,j) frame used for vpMESite
156 iP.set_ij(p_me.ifloat, p_me.jfloat);
157 p_me.alpha = computeTheta(iP);
158 *it = p_me;
159 }
160 }
161
162 /*!
163 Compute the coordinates of a point on an ellipse from its angle with respect
164 to the main orientation of the ellipse.
165
166 \param angle : Angle on the ellipse with respect to its major axis.
167 \param iP : Image point on the ellipse.
168 */
computePointOnEllipse(const double angle,vpImagePoint & iP)169 void vpMeEllipse::computePointOnEllipse(const double angle, vpImagePoint &iP)
170 {
171 // Two versions are available. If you change from one version to the other
172 // one, do not forget to change also the reciprocical function
173 // computeAngleOnEllipse() just below and, for a correct display of an arc
174 // of ellipse, adapt vpMeEllipse::display() below and
175 // vp_display_display_ellipse() in modules/core/src/display/vpDisplay_impl.h
176 // so that the two extremities of the arc are correctly shown.
177
178 // Version that gives a regular angular sampling on the ellipse, so less
179 // points at its extremities
180 /*
181 double co = cos(angle);
182 double si = sin(angle);
183 double coef = a * b / sqrt(b * b * co * co + a * a * si * si);
184 double u = co * coef;
185 double v = si * coef;
186 iP.set_u(uc + ce * u - se * v);
187 iP.set_v(vc + se * u + ce * v);
188 */
189
190 // Version from "the two concentric circles" method that gives more points
191 // at the ellipse extremities for a regular angle sampling. It is better to
192 // display an ellipse, not necessarily to track it
193
194 // (u,v) are the coordinates on the canonical centered ellipse;
195 double u = a * cos(angle);
196 double v = b * sin(angle);
197 // a rotation of e and a translation by (uc,vc) are done
198 // to get the coordinates of the point on the shifted ellipse
199 iP.set_uv(m_uc + ce * u - se * v, m_vc + se * u + ce * v);
200 }
201
202 /*!
203 Compute the angle of a point on the ellipse wrt the ellipse major axis.
204 \param pt : Image point on the ellipse.
205 \return The computed angle.
206 */
computeAngleOnEllipse(const vpImagePoint & pt) const207 double vpMeEllipse::computeAngleOnEllipse(const vpImagePoint &pt) const
208 {
209 // Two versions are available. If you change from one version to the other
210 // one, do not forget to change also the reciprocical function
211 // computePointOnEllipse() just above. Adapt also the display; see comment
212 // at the beginning of computePointOnEllipse()
213
214 // Regular angle smapling method
215 /*
216 double du = pt.get_u() - uc;
217 double dv = pt.get_v() - vc;
218 double ang = atan2(dv,du) - e;
219 if (ang > M_PI) {
220 ang -= 2.0 * M_PI;
221 }
222 else if (ang < -M_PI) {
223 ang += 2.0 * M_PI;
224 }
225 */
226 // for the "two concentric circles method" starting from the previous one
227 // (just to remember the link between both methods:
228 // tan(theta_2cc) = a/b tan(theta_rs))
229 /*
230 double co = cos(ang);
231 double si = sin(ang);
232 double coeff = 1.0/sqrt(b*b*co*co+a*a*si*si);
233 si *= a*coeff;
234 co *= b*coeff;
235 ang = atan2(si,co);
236 */
237 // For the "two concentric circles" method starting from scratch
238 double du = pt.get_u() - m_uc;
239 double dv = pt.get_v() - m_vc;
240 double co = (du * ce + dv * se)/a;
241 double si = (- du * se + dv * ce)/b;
242 double angle = atan2(si,co);
243
244 return(angle);
245 }
246
247 /*!
248 Computes the length of the semimajor axis \f$ a \f$, the length of the
249 semiminor axis \f$ b \f$, and \f$ e \f$ that is the angle
250 made by the major axis and the u axis of the image frame \f$ (u,v) \f$.
251 They are computed from the normalized moments $ \f$ n_{ij} \f$.
252 */
computeAbeFromNij()253 void vpMeEllipse::computeAbeFromNij()
254 {
255 double num = m_n20 - m_n02;
256 double d = num * num + 4.0 * m_n11 * m_n11; // always >= 0
257 if (d <= std::numeric_limits<double>::epsilon()) {
258 e = 0.0; // case n20 = n02 and n11 = 0 : circle, e undefined
259 ce = 1.0;
260 se = 0.0;
261 a = b = 2.0*sqrt(m_n20); // = sqrt(2.0*(n20+n02))
262 }
263 else { // real ellipse
264 e = atan2(2.0*m_n11, num)/2.0; // e in [-Pi/2 ; Pi/2]
265 ce = cos(e);
266 se = sin(e);
267
268 d = sqrt(d); // d in sqrt always >= 0
269 num = m_n20 + m_n02;
270 a = sqrt(2.0*(num + d)); // term in sqrt always > 0
271 b = sqrt(2.0*(num - d)); // term in sqrt always > 0
272 }
273 }
274
275 /*!
276 Computes the parameters \f$ K = {K_0, ..., K_5} \f$ from the center of
277 the ellipse and the normalized moments \f$ n_{ij} \f$. The parameters
278 \f$ K \f$ are such that \f$ K0 = n02, K1 = n20 \f$, etc. as in Eq (25)
279 of Chaumette 2004 TRO paper.
280 */
computeKiFromNij()281 void vpMeEllipse::computeKiFromNij()
282 {
283 K[0] = m_n02;
284 K[1] = m_n20;
285 K[2] = -m_n11;
286 K[3] = m_n11 * m_vc - m_n02 * m_uc;
287 K[4] = m_n11 * m_uc - m_n20 * m_vc;
288 K[5] = m_n02 * m_uc * m_uc + m_n20 * m_vc * m_vc - 2.0 * m_n11 * m_uc * m_vc
289 + 4.0 * (m_n11 * m_n11 - m_n20 * m_n02);
290 }
291
292 /*!
293 Computes the normalized moments \f$ n_{ij} \f$ from the \f$ A, B, E \f$
294 parameters as in Eq (24) of Chaumette 2004 TRO paper after simplifications
295 to deal with the case cos(e) = 0.0
296 */
computeNijFromAbe()297 void vpMeEllipse::computeNijFromAbe()
298 {
299 m_n20 = 0.25 * (a * a * ce * ce + b * b * se * se);
300 m_n11 = 0.25 * se * ce * (a * a - b * b);
301 m_n02 = 0.25 * (a * a * se * se + b * b * ce * ce);
302 }
303
304 /*!
305 Computes the coordinates of the ellipse center, the normalized
306 moments \f$ n_{ij} \f$, the length of the semimajor axis \f$ a \f$, the
307 length of the semiminor axis \f$ b \f$, and \f$ e \f$ that is the angle
308 made by the major axis and the u axis of the image frame \f$ (u,v) \f$.
309
310 All those computations are made from the parameters \f$ K ={K_0, ..., K_5} \f$
311 so that \f$ K_0 u^2 + K1 v^2 + 2 K2 u v + 2 K3 u + 2 K4 v + K5 = 0 \f$.
312 */
getParameters()313 void vpMeEllipse::getParameters()
314 {
315 // Equations below from Chaumette PhD and TRO 2004 paper
316 double num = K[0] * K[1] - K[2] * K[2]; // > 0 for an ellipse
317 if (num <= 0) {
318 throw(vpException(vpException::fatalError, "The points do not belong to an ellipse!"));
319 }
320
321 m_uc = (K[2] * K[4] - K[1] * K[3]) / num;
322 m_vc = (K[2] * K[3] - K[0] * K[4]) / num;
323 iPc.set_uv(m_uc, m_vc);
324
325 double d = (K[0] * m_uc * m_uc + K[1] * m_vc * m_vc + 2.0 * K[2] * m_uc * m_vc - K[5])
326 / (4.0 * num);
327 m_n20 = K[1] * d; // always > 0
328 m_n11 = -K[2] * d;
329 m_n02 = K[0] * d; // always > 0
330
331 computeAbeFromNij();
332
333 // normalization so that K0 = n02, K1 = n20, etc (Eq (25) of TRO paper)
334 d = m_n02/K[0]; // fabs(K[0]) > 0
335 for (unsigned int i=0; i < 6; i++) {
336 K[i] *= d;
337 }
338 if (vpDEBUG_ENABLE(3)) {
339 printParameters();
340 }
341 }
342
343 /*!
344 Print the parameters \f$ K = {K_0, ..., K_5} \f$, the coordinates of the
345 ellipse center, the normalized moments, and the A, B, E parameters.
346 */
printParameters() const347 void vpMeEllipse::printParameters() const
348 {
349 std::cout << "K :" << K.t() << std::endl;
350 std::cout << "xc = " << m_uc << ", yc = " << m_vc << std::endl;
351 std::cout << "n20 = " << m_n20 << ", n11 = " << m_n11 << ", n02 = " << m_n02 <<std::endl;
352 std::cout << "A = " << a << ", B = " << b << ", E (dg) " << vpMath::deg(e) <<std::endl;
353 }
354
355 /*!
356 Construct a list of vpMeSite moving edges at a particular sampling
357 step between the two extremities. The two extremities are defined by
358 the points with the smallest and the biggest \f$ alpha \f$ angle.
359
360 \param I : Image in which the ellipse appears.
361 \param doNotTrack : If true, moving-edges are not tracked.
362
363 \exception vpTrackingException::initializationError : Moving edges not
364 initialized.
365
366 */
sample(const vpImage<unsigned char> & I,bool doNotTrack)367 void vpMeEllipse::sample(const vpImage<unsigned char> &I, bool doNotTrack)
368 {
369 // Warning: similar code in vpMbtMeEllipse::sample()
370 if (!me) {
371 throw(vpException(vpException::fatalError, "Moving edges on ellipse not initialized"));
372 }
373 // Delete old lists
374 list.clear();
375 angle.clear();
376
377 int nbrows = static_cast<int>(I.getHeight());
378 int nbcols = static_cast<int>(I.getWidth());
379
380 if (std::fabs(me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
381 std::cout << "In vpMeEllipse::sample: ";
382 std::cout << "function called with sample step = 0, set to 10 dg";
383 me->setSampleStep(10.0);
384 }
385 double incr = vpMath::rad(me->getSampleStep()); // angle increment
386 // alpha2 - alpha1 = 2 * M_PI for a complete ellipse
387 m_expectedDensity = static_cast<unsigned int>(floor((alpha2 - alpha1) / incr));
388 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
389 expecteddensity = static_cast<double>(m_expectedDensity);
390 #endif
391
392 // starting angle for sampling
393 double ang = alpha1 + ((alpha2 - alpha1) - static_cast<double>(m_expectedDensity) * incr)/2.0;
394 // sample positions
395 for (unsigned int i = 0; i < m_expectedDensity; i++) {
396 vpImagePoint iP;
397 computePointOnEllipse(ang,iP);
398 // If point is in the image, add to the sample list
399 // Check done in (i,j) frame)
400 if (!outOfImage(vpMath::round(iP.get_i()), vpMath::round(iP.get_j()), 0, nbrows, nbcols)) {
401 vpDisplay::displayCross(I, iP, 5, vpColor::red);
402
403 double theta = computeTheta(iP);
404 vpMeSite pix;
405 // (i,j) frame used for vpMeSite
406 pix.init(iP.get_i(), iP.get_j(), theta);
407 pix.setDisplay(selectDisplay);
408 pix.setState(vpMeSite::NO_SUPPRESSION);
409 list.push_back(pix);
410 angle.push_back(ang);
411 }
412 ang += incr;
413 }
414 if (!doNotTrack) {
415 vpMeTracker::initTracking(I);
416 }
417 }
418
419 /*!
420 Seek along the ellipse or arc of ellipse its two extremities to try
421 recovering lost points. Try also to complete the parts with no tracked points.
422
423 \param I : Image in which the ellipse appears.
424
425 \return The function returns the number of points added to the list.
426
427 \exception vpTrackingException::initializationError : Moving edges not
428 initialized.
429 */
plugHoles(const vpImage<unsigned char> & I)430 unsigned int vpMeEllipse::plugHoles(const vpImage<unsigned char> &I)
431 {
432 if (!me) {
433 throw(vpException(vpException::fatalError, "Moving edges on ellipse tracking not initialized"));
434 }
435 unsigned int nb_pts_added = 0;
436 int nbrows = static_cast<int>(I.getHeight());
437 int nbcols = static_cast<int>(I.getWidth());
438
439 unsigned int memory_range = me->getRange();
440 me->setRange(2);
441 double memory_mu1 = me->getMu1();
442 me->setMu1(0.5);
443 double memory_mu2 = me->getMu2();
444 me->setMu2(0.5);
445
446 double incr = vpMath::rad(me->getSampleStep());
447 // Detect holes and try to complete them
448 // FC : Currently only one point is looked at the middle of each hole
449 // (to avoid multiple insertions that are time consuming).
450 // A different choice could be done.
451 std::list<double>::iterator angleList = angle.begin();
452 double ang = *angleList;
453 for (std::list<vpMeSite>::iterator meList = list.begin(); meList != list.end();) {
454 ++angleList;
455 ++meList;
456 double nextang = *angleList;
457 // The minimal size of a hole (1 point lost for sure).
458 // could be increased to reduce time processing
459 if ((nextang - ang) > 1.6 * incr) {
460 ang = (nextang + ang) / 2.0; // mid angle
461 vpImagePoint iP;
462 computePointOnEllipse(ang,iP);
463 if (!outOfImage(vpMath::round(iP.get_i()), vpMath::round(iP.get_j()), 0, nbrows, nbcols)) {
464 double theta = computeTheta(iP);
465 vpMeSite pix;
466 pix.init(iP.get_i(), iP.get_j(), theta);
467 pix.setDisplay(selectDisplay);
468 pix.setState(vpMeSite::NO_SUPPRESSION);
469 pix.track(I, me, false);
470 if (pix.getState() == vpMeSite::NO_SUPPRESSION) { // good point
471 nb_pts_added ++;
472 iP.set_ij(pix.ifloat, pix.jfloat);
473 double new_ang = computeAngleOnEllipse(iP);
474 if ((new_ang - ang) > M_PI) {
475 new_ang -= 2.0 * M_PI;
476 }
477 else if ((ang - new_ang) > M_PI) {
478 new_ang += 2.0 * M_PI;
479 }
480 list.insert(meList,pix);
481 angle.insert(angleList,new_ang);
482 if (vpDEBUG_ENABLE(3)) {
483 vpDisplay::displayCross(I, iP, 5, vpColor::blue);
484 }
485 }
486 }
487 }
488 ang = nextang;
489 }
490
491 // Try to fill the first extremity: from alpha_min - incr to alpha1
492 unsigned int nbpts = static_cast<unsigned int>(floor((m_alphamin-alpha1)/incr));
493 ang = m_alphamin - incr;
494 for (unsigned int i = 0; i < nbpts; i++) {
495 vpImagePoint iP;
496 computePointOnEllipse(ang ,iP);
497 if (!outOfImage(vpMath::round(iP.get_i()), vpMath::round(iP.get_j()), 0, nbrows, nbcols)) {
498 double theta = computeTheta(iP);
499 vpMeSite pix;
500 pix.init(iP.get_i(), iP.get_j(), theta);
501 pix.setDisplay(selectDisplay);
502 pix.setState(vpMeSite::NO_SUPPRESSION);
503 pix.track(I, me, false);
504 if (pix.getState() == vpMeSite::NO_SUPPRESSION) {
505 nb_pts_added ++;
506 iP.set_ij(pix.ifloat, pix.jfloat);
507 double new_ang = computeAngleOnEllipse(iP);
508 if ((new_ang - ang) > M_PI) {
509 new_ang -= 2.0 * M_PI;
510 }
511 else if ((ang - new_ang) > M_PI) {
512 new_ang += 2.0 * M_PI;
513 }
514 list.push_front(pix);
515 angle.push_front(new_ang);
516 if (vpDEBUG_ENABLE(3)) {
517 vpDisplay::displayCross(I, iP, 5, vpColor::blue);
518 }
519 }
520 }
521 ang -= incr;
522 }
523
524 // Try to fill the second extremity: from alphamax + incr to alpha2
525 nbpts = static_cast<unsigned int>(floor((alpha2-m_alphamax)/incr));
526 ang = m_alphamax + incr;
527 for (unsigned int i = 0; i < nbpts; i++) {
528 vpImagePoint iP;
529 computePointOnEllipse(ang,iP);
530 if (!outOfImage(vpMath::round(iP.get_i()), vpMath::round(iP.get_j()), 0, nbrows, nbcols)) {
531 double theta = computeTheta(iP);
532 vpMeSite pix;
533 pix.init(iP.get_i(), iP.get_j(), theta);
534 pix.setDisplay(selectDisplay);
535 pix.setState(vpMeSite::NO_SUPPRESSION);
536 pix.track(I, me, false);
537 if (pix.getState() == vpMeSite::NO_SUPPRESSION) {
538 nb_pts_added ++;
539 iP.set_ij(pix.ifloat, pix.jfloat);
540 double new_ang = computeAngleOnEllipse(iP);
541 if ((new_ang - ang) > M_PI) {
542 new_ang -= 2.0 * M_PI;
543 }
544 else if ((ang - new_ang) > M_PI) {
545 new_ang += 2.0 * M_PI;
546 }
547 list.push_back(pix);
548 angle.push_back(new_ang);
549 if (vpDEBUG_ENABLE(3)) {
550 vpDisplay::displayCross(I, iP, 5, vpColor::blue);
551 }
552 }
553 }
554 ang += incr;
555 }
556 me->setRange(memory_range);
557 me->setMu1(memory_mu1);
558 me->setMu2(memory_mu2);
559
560 if (vpDEBUG_ENABLE(3)) {
561 printf("In plugHoles(): nb of added points : %d\n", nb_pts_added);
562 }
563 return nb_pts_added;
564 }
565
566 /*!
567 Least squares method to compute the ellipse to which the points belong.
568
569 \param I : Image in which the ellipse appears (useful just to get its number
570 of rows and columns...
571 \param iP : A vector of points belonging to the ellipse.
572 */
leastSquare(const vpImage<unsigned char> & I,const std::vector<vpImagePoint> & iP)573 void vpMeEllipse::leastSquare(const vpImage<unsigned char> &I, const std::vector<vpImagePoint> &iP)
574 {
575 // Homogeneous system A x = 0 ; x is the nullspace of A
576 // K0 u^2 + K1 v^2 + 2 K2 u v + 2 K3 u + 2 K4 v + K5 = 0
577 // A = (u^2 v^2 2uv 2u 2v 1), x = (K0 K1 K2 K3 K4 K5)^T
578
579 // It would be a bad idea to solve the same system using A x = b where
580 // A = (u^2 v^2 2uv 2u 2v), b = (-1), x = (K0 K1 K2 K3 K4)^T since it
581 // cannot consider the case where the origin belongs to the ellipse.
582 // Another possibility would be to consider K0+K1=1 which is always valid,
583 // leading to the system A x = b where
584 // A = (u^2-v^2 2uv 2u 2v 1), b = (-v^2), x = (K0 K2 K3 K4 K5)^T
585
586 double um = I.getWidth() / 2.;
587 double vm = I.getHeight() / 2.;
588 unsigned int n = static_cast<unsigned int>(iP.size());
589
590 vpMatrix A(n, 6);
591
592 for (unsigned int k = 0; k < n; k++) {
593 // normalization so that (u,v) in [-1;1]
594 double u = (iP[k].get_u() - um) / um;
595 double v = (iP[k].get_v() - vm) / vm;
596 A[k][0] = u * u;
597 A[k][1] = v * v;
598 A[k][2] = 2.0 * u * v;
599 A[k][3] = 2.0 * u;
600 A[k][4] = 2.0 * v;
601 A[k][5] = 1.0;
602 }
603 vpMatrix KerA;
604 unsigned int dim = A.nullSpace(KerA, 1);
605 if (dim > 1) { // case with less than 5 independent points
606 // FC : should create a rankError exception
607 throw(vpException(vpException::fatalError, "Linear sytem for computing the ellipse equation ill conditionned"));
608 }
609 // the term um*vm is for counterbalancing the bad conditioning of the
610 // inverse normalization below
611 for (unsigned int i = 0; i < 6 ; i++) K[i] = um * vm * KerA[i][0];
612
613 // Inverse normalization to go back to pixel values
614 K[0] /= um * um;
615 K[1] /= vm * vm;
616 K[2] /= um * vm;
617 K[3] = K[3]/um - K[0] * um - K[2] * vm;
618 K[4] = K[4]/vm - K[1] * vm - K[2] * um;
619 K[5] = K[5] - K[0] * um * um - K[1] * vm * vm - 2.0 * K[2] * um * vm - 2.0 * K[3] * um - 2.0 * K[4] * vm;
620
621 getParameters();
622 }
623
624 /*!
625 Robust least squares method to compute the ellipse to which the vpMeSite
626 belong. Manage also the lists of vpMeSite and corresponding angles.
627
628 \param I : Image where tracking is done (useful just to get its number
629 of rows and columns...
630 */
leastSquareRobust(const vpImage<unsigned char> & I)631 void vpMeEllipse::leastSquareRobust(const vpImage<unsigned char> &I)
632 {
633 // Homogeneous system A x = 0 ; x is the nullspace of A
634 // K0 u^2 + K1 v^2 + 2 K2 u v + 2 K3 u + 2 K4 v + K5 = 0
635 // A = (u^2 v^2 2uv 2u 2v 1), x = (K0 K1 K2 K3 K4 K5)^T
636
637 // It would be a bad idea to solve the same system using A x = b where
638 // A = (u^2 v^2 2uv 2u 2v), b = (-1), x = (K0 K1 K2 K3 K4)^T since it
639 // cannot consider the case where the origin belongs to the ellipse.
640 // Another possibility would be to consider K0+K1=1 which is always valid,
641 // leading to the system A x = b where
642 // A = (u^2-v^2 2uv 2u 2v 1), b = (-v^2), x = (K0 K2 K3 K4 K5)^T
643
644 const unsigned int nos = numberOfSignal();
645
646 // Note that the (nos-k) last rows of A, xp and yp are not used.
647 // Hopefully, this is not an issue.
648
649 vpMatrix A(nos, 6);
650 // Useful to compute the weights in the robust estimation
651 vpColVector xp(nos), yp(nos);
652
653 unsigned int k = 0;
654 double um = I.getWidth() / 2.;
655 double vm = I.getHeight() / 2.;
656 for (std::list<vpMeSite>::const_iterator it = list.begin(); it != list.end(); ++it) {
657 vpMeSite p_me = *it;
658 if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
659 // from (i,j) to (u,v) frame + normalization so that (u,v) in [-1;1]
660 double u = (p_me.jfloat - um) / um;
661 double v = (p_me.ifloat - vm) / vm;
662 A[k][0] = u * u;
663 A[k][1] = v * v;
664 A[k][2] = 2.0 * u * v;
665 A[k][3] = 2.0 * u;
666 A[k][4] = 2.0 * v;
667 A[k][5] = 1.0;
668 // Useful to compute the weights in the robust estimation
669 xp[k] = p_me.jfloat;
670 yp[k] = p_me.ifloat;
671
672 k++;
673 }
674 }
675 if (k < 5) {
676 throw(vpException(vpException::dimensionError, "Not enough moving edges to track the ellipse"));
677 }
678
679 vpRobust r;
680 // r.setThreshold(0.02); // Old version where this threshold was highly
681 // sensitive since the residues do not represent the Euclidean distance
682 // from the point to the ellipse
683 r.setMinMedianAbsoluteDeviation(1.0); // image noise in pixels for the geometrical distance
684 vpColVector w(k);
685 w = 1.0;
686 unsigned int iter = 0;
687 double var = 1.0;
688 vpColVector Kprev(6);
689 vpMatrix DA(k, 6);
690 vpMatrix KerDA;
691
692 // stop after 4 it or if variation of K between 2 iterations is more than 0.1 %
693 while ((iter < 4) && (var > 0.001)) {
694 for (unsigned int i = 0; i < k ; i++) {
695 for (unsigned int j = 0; j < 6 ; j++) {
696 DA[i][j] = w[i] * A[i][j];
697 }
698 }
699 unsigned int dim = DA.nullSpace(KerDA, 1);
700 if (dim > 1) { // case with less than 5 independent points
701 // FC : should create a rankError exception
702 throw(vpException(vpException::fatalError, "Linear sytem for computing the ellipse equation ill conditionned"));
703 }
704
705 for (unsigned int i=0; i<6 ; i++) K[i] = KerDA[i][0]; // norm(K) = 1
706 var = (K-Kprev).frobeniusNorm();
707 Kprev = K;
708 // the term um*vm is for counterbalancing the bad conditioning of the
709 // inverse normalization just below
710 K *= (um * vm);
711 // vpColVector residu(k); // old version for considering the algebraic distance
712 // residu = A * K;
713 // Better version considering the geometric distance
714 // Inverse normalization to go back to pixels
715 K[0] /= um * um;
716 K[1] /= vm * vm;
717 K[2] /= um * vm;
718 K[3] = K[3]/um - K[0] * um - K[2] * vm;
719 K[4] = K[4]/vm - K[1] * vm - K[2] * um;
720 K[5] = K[5] - K[0] * um * um - K[1] * vm * vm - 2.0 * K[2] * um * vm - 2.0 * K[3] * um - 2.0 * K[4] * vm;
721 getParameters(); // since a, b, and e are used just after
722
723 vpColVector residu(k);
724 for (unsigned int i=0; i < k ; i++) {
725 vpImagePoint ip1, ip2;
726 ip1.set_uv(xp[i],yp[i]);
727 double ang = computeAngleOnEllipse(ip1);
728 computePointOnEllipse(ang, ip2);
729 // residu = 0 if point is exactly on the ellipse, not otherwise
730 residu[i] = vpImagePoint::distance(ip1, ip2);
731 }
732 // end of new version
733 r.MEstimator(vpRobust::TUKEY, residu, w);
734 iter++;
735 }
736 /* FC : for old version with algebraic distance
737 // Inverse normalization to go back to pixels
738 K[0] /= um * um;
739 K[1] /= vm * vm;
740 K[2] /= um * vm;
741 K[3] = K[3]/um - K[0] * um - K[2] * vm;
742 K[4] = K[4]/vm - K[1] * vm - K[2] * um;
743 K[5] = K[5] - K[0] * um * um - K[1] * vm * vm - 2.0 * K[2] * um * vm - 2.0 * K[3] * um - 2.0 * K[4] * vm;
744 getParameters();
745 */
746 double previous_ang = - 4.0 * M_PI;
747 double incr = vpMath::rad(me->getSampleStep());
748 // Remove bad points, too near points, and outliers from the lists
749 k = m_numberOfGoodPoints = 0;
750 std::list<double>::iterator angleList = angle.begin();
751 for (std::list<vpMeSite>::iterator meList = list.begin(); meList != list.end();) {
752 vpMeSite p_me = *meList;
753 if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
754 if (w[k] > thresholdWeight) { // inlier
755 // Management of the angle to keep only the points in the interval
756 // [alpha1 ; alpha2]
757 double ang = *angleList;
758 vpImagePoint iP;
759 iP.set_ij(p_me.ifloat,p_me.jfloat);
760 double new_ang = computeAngleOnEllipse(iP);
761 if ((new_ang - ang) > M_PI) {
762 new_ang -= 2.0 * M_PI;
763 }
764 else if ((ang - new_ang) > M_PI) {
765 new_ang += 2.0 * M_PI;
766 }
767 if ((new_ang >= alpha1) && (new_ang <= alpha2) ) {
768 // good point if not too near from the previous one in the list
769 // if so, udate of its angle
770 if ((new_ang - previous_ang) >= (0.6 * incr)) {
771 *angleList = previous_ang = new_ang;
772 m_numberOfGoodPoints++;
773 ++meList;
774 ++angleList;
775 if (vpDEBUG_ENABLE(3)) {
776 vpDisplay::displayCross(I, iP, 10, vpColor::red, 1);
777 printf("In LQR: angle : %lf, i = %.0lf, j = %.0lf\n",
778 vpMath::deg(new_ang), iP.get_i(), iP.get_j());
779 }
780 }
781 else {
782 if (vpDEBUG_ENABLE(3)) {
783 vpDisplay::displayCross(I, iP, 10, vpColor::orange, 1);
784 printf("too near : angle %lf, i %.0f , j : %0.f\n",
785 vpMath::deg(new_ang), p_me.ifloat, p_me.jfloat);
786 }
787 meList = list.erase(meList);
788 angleList = angle.erase(angleList);
789 }
790 }
791 else { // point not in the interval [alpha1 ; alpha2]
792 if (vpDEBUG_ENABLE(3)) {
793 vpDisplay::displayCross(I, iP, 10, vpColor::green, 1);
794 printf("not in interval: angle : %lf, i %.0f , j : %0.f\n",
795 vpMath::deg(new_ang), p_me.ifloat, p_me.jfloat);
796 }
797 meList = list.erase(meList);
798 angleList = angle.erase(angleList);
799 }
800 }
801 else { // outlier
802 if (vpDEBUG_ENABLE(3)) {
803 vpImagePoint iP;
804 iP.set_ij(p_me.ifloat,p_me.jfloat);
805 printf("point %d outlier i : %.0f , j : %0.f, poids : %lf\n",
806 k, p_me.ifloat, p_me.jfloat, w[k]);
807 vpDisplay::displayCross(I, iP, 10, vpColor::cyan, 1);
808 }
809 meList = list.erase(meList);
810 angleList = angle.erase(angleList);
811 }
812 k++;
813 }
814 else { // points not selected as me
815 meList = list.erase(meList);
816 angleList = angle.erase(angleList);
817 if (vpDEBUG_ENABLE(3)) {
818 vpImagePoint iP;
819 iP.set_ij(p_me.ifloat,p_me.jfloat);
820 printf("point not me i : %.0f , j : %0.f\n", p_me.ifloat, p_me.jfloat);
821 vpDisplay::displayCross(I, iP, 10, vpColor::blue, 1);
822 }
823 }
824 }
825 // set extremities of the angle list
826 m_alphamin = angle.front();
827 m_alphamax = angle.back();
828
829 if (vpDEBUG_ENABLE(3)) {
830 printf("alphamin : %lf, alphamax : %lf\n", vpMath::deg(m_alphamin), vpMath::deg(m_alphamax));
831 printf("dans leastSquareRobust : nb pts ok = %d \n", m_numberOfGoodPoints);
832 }
833 }
834
835 /*!
836 Display the ellipse or arc of ellipse
837
838 \warning To effectively display the ellipse a call to
839 vpDisplay::flush() is needed.
840
841 \param I : Image in which the ellipse appears.
842 \param col : Color of the displayed ellipse.
843 */
display(const vpImage<unsigned char> & I,vpColor col)844 void vpMeEllipse::display(const vpImage<unsigned char> &I, vpColor col)
845 {
846 vpMeEllipse::display(I, iPc, a, b, e, alpha1, alpha2, col);
847 }
848
849 /*!
850 Initialize the tracking of an ellipse or an arc of an ellipse when \e trackArc is set to true.
851 Ask the user to click on five points located on the ellipse to be tracked.
852
853 \warning The points should be selected as far as possible from each other.
854 When an arc of an ellipse is tracked, it is recommended to select the 5 points clockwise.
855
856 \param I : Image in which the ellipse appears.
857 \param trackArc : When true, track an arc of the ellipse.
858 First and fifth points specify the extremities of the arc (clockwise).
859 When false track the complete ellipse.
860 */
initTracking(const vpImage<unsigned char> & I,bool trackArc)861 void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, bool trackArc)
862 {
863 const unsigned int n = 5;
864 std::vector<vpImagePoint> iP(n);
865 m_trackArc = trackArc;
866
867 if (m_trackArc) {
868 std::cout << "First and fifth points specify the extremities of the arc of ellipse (clockwise)" << std::endl;
869 }
870 for (unsigned int k = 0; k < n; k++) {
871 std::cout << "Click point " << k + 1 << "/" << n << " on the ellipse " << std::endl;
872 vpDisplay::getClick(I, iP[k], true);
873 vpDisplay::displayCross(I, iP[k], 10, vpColor::red);
874 vpDisplay::flush(I);
875 std::cout << iP[k] << std::endl;
876 }
877 initTracking(I, iP, trackArc);
878 }
879
880 /*!
881 Initialize the tracking of an ellipse or an arc of an ellipse when \e trackArc is set to true.
882 The ellipse is defined thanks to a vector of image points.
883
884 \warning It is mandatory to use at least five image points to estimate the
885 ellipse parameters.
886 \warning The image points should be selected as far as possible from each other.
887 When an arc of an ellipse is tracked, it is recommended to select the 5 points clockwise.
888
889 \param I : Image in which the ellipse appears.
890 \param iP : A vector of image points belonging to the ellipse edge used to
891 initialize the tracking.
892 \param trackArc : When true, track an arc of the ellipse.
893 First and last points specify the extremities of the arc (clockwise).
894 When false track the complete ellipse.
895 */
initTracking(const vpImage<unsigned char> & I,const std::vector<vpImagePoint> & iP,bool trackArc)896 void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const std::vector<vpImagePoint> &iP, bool trackArc)
897 {
898 m_trackArc = trackArc;
899 // useful for sample(I):
900 leastSquare(I, iP);
901 if (trackArc) {
902 // useful for track(I):
903 iP1 = iP.front();
904 iP2 = iP.back();
905 // useful for sample(I):
906 alpha1 = computeAngleOnEllipse(iP1);
907 alpha2 = computeAngleOnEllipse(iP2);
908 if ((alpha2 <= alpha1) || (std::fabs(alpha2 - alpha1) < m_arcEpsilon)) {
909 alpha2 += 2.0 * M_PI;
910 }
911 }
912 else {
913 alpha1 = 0.0;
914 alpha2 = 2 * M_PI;
915 // useful for track(I):
916 vpImagePoint ip;
917 computePointOnEllipse(alpha1, ip);
918 iP1 = iP2 = ip;
919 }
920
921 sample(I);
922 track(I);
923 vpMeTracker::display(I);
924 vpDisplay::flush(I);
925 }
926
927 /*!
928 Initialize the tracking of an ellipse or an arc of an ellipse when arc extremities are given.
929 The ellipse is defined by the vector containing the coordinates of its center and the three second order
930 centered normalized moments \f$ n_ij \f$. Without setting the arc extremities with
931 parameters \e pt1 and \e pt2, the complete ellipse is considered. When extremities
932 are set, we consider an ellipse arc defined clockwise from first extremity to second extremity.
933
934 \param I : Image in which the ellipse appears.
935 \param param : Vector with the five parameters \f$(u_c, v_c, n_{20}, n_{11}, n_{02})\f$ defining the ellipse
936 (expressed in pixels).
937 \param pt1 : Image point defining the first extremity of the arc or NULL to track a complete ellipse.
938 \param pt2 : Image point defining the second extremity of the arc or NULL to track a complete ellipse.
939
940 */
initTracking(const vpImage<unsigned char> & I,const vpColVector & param,vpImagePoint * pt1,const vpImagePoint * pt2)941 void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpColVector ¶m, vpImagePoint *pt1, const vpImagePoint *pt2)
942 {
943 if (pt1 != NULL && pt2 != NULL) {
944 m_trackArc = true;
945 }
946 // useful for sample(I) : uc, vc, a, b, e, Ki, alpha1, alpha2
947 m_uc = param[0];
948 m_vc = param[1];
949 m_n20 = param[2];
950 m_n11 = param[3];
951 m_n02 = param[4];
952 computeAbeFromNij();
953 computeKiFromNij();
954
955 if (m_trackArc) {
956 alpha1 = computeAngleOnEllipse(*pt1);
957 alpha2 = computeAngleOnEllipse(*pt2);
958 if ((alpha2 <= alpha1) || (std::fabs(alpha2 - alpha1) < m_arcEpsilon)) {
959 alpha2 += 2.0 * M_PI;
960 }
961 // useful for track(I)
962 iP1 = *pt1;
963 iP2 = *pt2;
964 }
965 else {
966 alpha1 = 0.0;
967 alpha2 = 2.0 * M_PI;
968 // useful for track(I)
969 vpImagePoint ip;
970 computePointOnEllipse(alpha1, ip);
971 iP1 = iP2 = ip;
972 }
973 // useful for display(I) so useless if no display before track(I)
974 iPc.set_uv(m_uc, m_vc);
975
976 sample(I);
977 track(I);
978 vpMeTracker::display(I);
979 vpDisplay::flush(I);
980 }
981
982 /*!
983 Track the ellipse in the image I.
984
985 \param I : Image in which the ellipse appears.
986 */
track(const vpImage<unsigned char> & I)987 void vpMeEllipse::track(const vpImage<unsigned char> &I)
988 {
989 vpMeTracker::track(I);
990
991 // recompute alpha1 and alpha2 in case they have been changed by setEndPoints()
992 if (m_trackArc) {
993 alpha1 = computeAngleOnEllipse(iP1);
994 alpha2 = computeAngleOnEllipse(iP2);
995 if ((alpha2 <= alpha1) || (std::fabs(alpha2 - alpha1) < m_arcEpsilon)) {
996 alpha2 += 2.0 * M_PI;
997 }
998 }
999 // Compute the ellipse parameters from the tracked points and manage the lists
1000 leastSquareRobust(I);
1001 if (vpDEBUG_ENABLE(3)) {
1002 printf("nb of Good points %u, density %d, alphamin %lf, alphamax %lf\n",
1003 m_numberOfGoodPoints, m_expectedDensity,
1004 vpMath::deg(m_alphamin), vpMath::deg(m_alphamax));
1005 }
1006
1007 // Try adding points at the extremities and in the holes if needed
1008 if (m_numberOfGoodPoints < m_expectedDensity) { // at least one point has been lost
1009 if (plugHoles(I) > 0) {
1010 leastSquareRobust(I); // if new points have been added, recompute the ellipse parameters and manage again the lists
1011 }
1012 }
1013 if (vpDEBUG_ENABLE(3)) {
1014 printf("nb of Good points %u, density %d, alphamin %lf, alphamax %lf\n",
1015 m_numberOfGoodPoints, m_expectedDensity,
1016 vpMath::deg(m_alphamin), vpMath::deg(m_alphamax));
1017 }
1018
1019 // resample if needed in case of unsufficient number of points or
1020 // bad repartition
1021 // FC : (thresholds ad hoc and sensitive...)
1022 if ( ((3 * m_numberOfGoodPoints) < m_expectedDensity) || (m_numberOfGoodPoints <= 5) || ( (m_alphamax - m_alphamin) < vpMath::rad(120.0) ) ) {
1023 if (vpDEBUG_ENABLE(3)) {
1024 printf("Before RESAMPLE !!! nb points %d \n", m_numberOfGoodPoints);
1025 printf("A click to continue \n");
1026 vpDisplay::flush(I);
1027 vpDisplay::getClick(I);
1028 vpDisplay::display(I);
1029 }
1030
1031 sample(I);
1032 vpMeTracker::track(I);
1033 leastSquareRobust(I);
1034 if (vpDEBUG_ENABLE(3)) {
1035 printf("nb of Good points %u, density %d, alphamax %lf, alphamin %lf\n",
1036 m_numberOfGoodPoints, m_expectedDensity, vpMath::deg(m_alphamax), vpMath::deg(m_alphamin));
1037 }
1038
1039 // Stop in case of failure after resample
1040 if ( ((3 * m_numberOfGoodPoints) < m_expectedDensity) || (m_numberOfGoodPoints <= 5) || ( (m_alphamax - m_alphamin) < vpMath::rad(120.0) ) ) {
1041 // FC : dimensionError pas vraiment le bon terme...
1042 throw(vpException(vpException::dimensionError, "Impossible to track the ellipse"));
1043 }
1044 }
1045
1046 if (vpDEBUG_ENABLE(3)) {
1047 printParameters();
1048 }
1049 // remet a jour l'angle delta pour chaque vpMeSite de la liste
1050 updateTheta();
1051 // not in getParameters since computed only once for each image
1052 m00 = M_PI * a * b;
1053
1054 // Useful only for tracking an arc of ellipse, but done to give them a value
1055 computePointOnEllipse(alpha1, iP1);
1056 computePointOnEllipse(alpha2, iP2);
1057
1058 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
1059 computeMoments();
1060 #endif
1061
1062 if (vpDEBUG_ENABLE(3)) {
1063 display(I, vpColor::red);
1064 vpMeTracker::display(I);
1065 vpDisplay::flush(I);
1066 }
1067 }
1068
1069
1070 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
1071 /*!
1072 \deprecated Computes the first and second order moments \f$ m_{ij} \f$
1073 */
computeMoments()1074 void vpMeEllipse::computeMoments()
1075 {
1076 // m00 = M_PI * a * b; // moved in track(I)
1077
1078 m10 = m_uc * m00;
1079 m01 = m_vc * m00;
1080
1081 mu20 = m_n20 * m00;
1082 mu11 = m_n11 * m00;
1083 mu02 = m_n02 * m00;
1084
1085 m20 = mu20 + m10 * m_uc;
1086 m11 = mu11 + m10 * m_vc;
1087 m02 = mu02 + m01 * m_vc;
1088 }
1089
1090 /*!
1091 \deprecated Initialization of the tracking. The arc of the ellipse is defined thanks to
1092 its center, semimajor axis, semiminor axis, orientation and the angle
1093 of its two extremities
1094
1095 \param I : Image in which the ellipse appears.
1096 \param center_p : Ellipse center.
1097 \param a_p : Semimajor axis.
1098 \param b_p : Semiminor axis.
1099 \param e_p : Orientationn in rad.
1100 \param alpha1_p : Angle in rad defining the first extremity of the arc.
1101 \param alpha2_p : Angle in rad defining the second extremity of the arc.
1102 */
initTracking(const vpImage<unsigned char> & I,const vpImagePoint & center_p,double a_p,double b_p,double e_p,double alpha1_p,double alpha2_p)1103 void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpImagePoint ¢er_p,
1104 double a_p, double b_p, double e_p, double alpha1_p, double alpha2_p)
1105 {
1106 m_trackArc = true;
1107 // useful for sample(I): uc, vc, a, b, e, Ki, alpha1, alpha2
1108 m_uc = center_p.get_u();
1109 m_vc = center_p.get_v();
1110 a = a_p;
1111 b = b_p;
1112 e = e_p;
1113 ce = cos(e);
1114 se = sin(e);
1115 computeNijFromAbe();
1116 computeKiFromNij();
1117
1118 alpha1 = alpha1_p;
1119 alpha2 = alpha2_p;
1120 if (alpha2 < alpha1) {
1121 alpha2 += 2 * M_PI;
1122 }
1123 // useful for track(I)
1124 vpImagePoint ip;
1125 computePointOnEllipse(alpha1, ip);
1126 iP1 = ip;
1127 computePointOnEllipse(alpha2, ip);
1128 iP2 = ip;
1129 // currently not used after, but done to be complete
1130 // would be needed for displaying the ellipse here
1131 iPc = center_p;
1132
1133 sample(I);
1134 track(I);
1135 vpMeTracker::display(I);
1136 vpDisplay::flush(I);
1137 }
1138
1139 /*!
1140 \deprecated Initialization of the tracking. The ellipse is defined thanks to the
1141 coordinates of n points.
1142
1143 \warning It is mandatory to use at least five points to estimate the
1144 ellipse parameters.
1145 \warning The n points should be selected as far as possible from each other.
1146
1147 \param I : Image in which the ellipse appears.
1148 \param n : The number of points in the list.
1149 \param iP : A pointer to a list of points belonging to the ellipse edge.
1150 */
initTracking(const vpImage<unsigned char> & I,unsigned int n,vpImagePoint * iP)1151 void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, unsigned int n, vpImagePoint *iP)
1152 {
1153 std::vector<vpImagePoint> v_iP(n);
1154
1155 for (unsigned int i = 0; i < n; i++) {
1156 v_iP[i] = iP[i];
1157 }
1158 initTracking(I, v_iP);
1159 }
1160
1161 /*!
1162 * \deprecated Use an other initTracking() function.
1163 */
initTracking(const vpImage<unsigned char> & I,unsigned int n,unsigned * i,unsigned * j)1164 void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, unsigned int n, unsigned *i, unsigned *j)
1165 {
1166 std::vector<vpImagePoint> v_iP(n);
1167
1168 for (unsigned int k=0; k < n; k++) {
1169 v_iP[k].set_ij(i[k],j[k]);
1170 }
1171 initTracking(I, v_iP);
1172 }
1173 #endif // Deprecated
1174
1175 /*!
1176 Display the ellipse or the arc of ellipse thanks to the ellipse parameters.
1177
1178 \param I : The image used as background.
1179
1180 \param center : Center of the ellipse.
1181
1182 \param A : Semimajor axis of the ellipse.
1183
1184 \param B : Semiminor axis of the ellipse.
1185
1186 \param E : Angle made by the major axis and the u axis of the image frame
1187 \f$ (u,v) \f$ (in rad).
1188
1189 \param smallalpha : Smallest \f$ alpha \f$ angle in rad (0 for a complete ellipse).
1190
1191 \param highalpha : Highest \f$ alpha \f$ angle in rad (2 \f$ \Pi \f$ for a complete ellipse).
1192
1193 \param color : Color used to display the ellipse.
1194
1195 \param thickness : Thickness of the drawings.
1196 */
display(const vpImage<unsigned char> & I,const vpImagePoint & center,const double & A,const double & B,const double & E,const double & smallalpha,const double & highalpha,const vpColor & color,unsigned int thickness)1197 void vpMeEllipse::display(const vpImage<unsigned char> &I,
1198 const vpImagePoint ¢er, const double &A, const double &B, const double &E,
1199 const double &smallalpha, const double &highalpha,
1200 const vpColor &color, unsigned int thickness)
1201 {
1202 vpDisplay::displayEllipse(I, center, A, B, E, smallalpha, highalpha, false, color, thickness, true, true);
1203 }
1204
1205 /*!
1206
1207 Display the ellipse or the arc of ellipse thanks to the ellipse parameters.
1208
1209 \param I : The image used as background.
1210
1211 \param center : Center of the ellipse
1212
1213 \param A : Semimajor axis of the ellipse.
1214
1215 \param B : Semiminor axis of the ellipse.
1216
1217 \param E : Angle made by the major axis and the u axis of the image frame
1218 \f$ (u,v) \f$ (in rad)
1219
1220 \param smallalpha : Smallest \f$ alpha \f$ angle in rad (0 for a complete ellipse)
1221
1222 \param highalpha : Highest \f$ alpha \f$ angle in rad (\f$ 2 \Pi \f$ for a complete ellipse)
1223
1224 \param color : Color used to display th lines.
1225
1226 \param thickness : Thickness of the drawings.
1227 */
display(const vpImage<vpRGBa> & I,const vpImagePoint & center,const double & A,const double & B,const double & E,const double & smallalpha,const double & highalpha,const vpColor & color,unsigned int thickness)1228 void vpMeEllipse::display(const vpImage<vpRGBa> &I,
1229 const vpImagePoint ¢er, const double &A, const double &B, const double &E,
1230 const double &smallalpha, const double &highalpha,
1231 const vpColor &color, unsigned int thickness)
1232 {
1233 vpDisplay::displayEllipse(I, center, A, B, E, smallalpha, highalpha, false, color, thickness, true, true);
1234 }
1235