Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpMeEllipse.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2024 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 <cmath> // std::fabs
32#include <limits> // numeric_limits
33#include <vector>
34
35#include <visp3/core/vpMatrixException.h>
36#include <visp3/core/vpTrackingException.h>
37#include <visp3/core/vpImagePoint.h>
38#include <visp3/me/vpMe.h>
39#include <visp3/me/vpMeEllipse.h>
40
41#ifndef VP_ME_ELLIPSE_REGULAR_SAMPLING
42#define VP_ME_ELLIPSE_TWO_CONCENTRIC_CIRCLES
43#endif
44
46
48 : m_K(), m_iPc(), m_a(0.), m_b(0.), m_e(0.), m_iP1(), m_iP2(), m_alpha1(0), m_ce(0.), m_se(0.), m_angleList(), m_m00(0.),
49 m_thresholdWeight(0.2), m_alphamin(0.), m_alphamax(0.), m_uc(0.), m_vc(0.), m_n20(0.), m_n11(0.), m_n02(0.),
51{
52 const unsigned int val_2 = 2;
53 const unsigned int val_6 = 6;
54 m_alpha2 = val_2 * M_PI;
55 // Resize internal parameters vector
56 // K0 u^2 + K1 v^2 + 2 K2 u v + 2 K3 u + 2 K4 v + K5 = 0
57 m_K.resize(val_6);
58 m_iP1.set_ij(0, 0);
59 m_iP2.set_ij(0, 0);
60}
61
63 : vpMeTracker(me_ellipse)
64{
65 *this = me_ellipse;
66}
67
69{
70 m_meList.clear();
71 m_angleList.clear();
72}
73
75{
76 m_K = me_ellipse.m_K;
77 m_iPc = me_ellipse.m_iPc;
78 m_a = me_ellipse.m_a;
79 m_b = me_ellipse.m_b;
80 m_e = me_ellipse.m_e;
81 m_iP1 = me_ellipse.m_iP1;
82 m_iP2 = me_ellipse.m_iP2;
83 m_alpha1 = me_ellipse.m_alpha1;
84 m_alpha2 = me_ellipse.m_alpha2; m_ce = me_ellipse.m_ce;
85 m_se = me_ellipse.m_se;
86 m_angleList = me_ellipse.m_angleList;
87 m_m00 = me_ellipse.m_m00;
89 m_alphamin = me_ellipse.m_alphamin;
90 m_alphamax = me_ellipse.m_alphamax;
91 m_uc = me_ellipse.m_uc;
92 m_vc = me_ellipse.m_vc;
93 m_n20 = me_ellipse.m_n20;
94 m_n11 = me_ellipse.m_n11;
95 m_n02 = me_ellipse.m_n02;
98 m_trackCircle = me_ellipse.m_trackCircle;
99 m_trackArc = me_ellipse.m_trackArc;
100 m_arcEpsilon = me_ellipse.m_arcEpsilon;
101
102 return *this;
103}
104
106{
107 double u = iP.get_u();
108 double v = iP.get_v();
109
110 return (computeTheta(u, v));
111}
112
113double vpMeEllipse::computeTheta(double u, double v) const
114{
115 double A = (m_K[0] * u) + (m_K[2] * v) + m_K[3];
116 double B = (m_K[1] * v) + (m_K[2] * u) + m_K[4];
117
118 double theta = atan2(B, A); // Angle between the tangent and the u axis.
119 if (theta < 0) { // theta in [0;Pi] // FC : pourquoi ? pour me sans doute
120 theta += M_PI;
121 }
122 return theta;
123}
124
126{
127 vpMeSite p_me;
128 vpImagePoint iP;
129 std::list<vpMeSite>::iterator end = m_meList.end();
130 for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != end; ++it) {
131 p_me = *it;
132 // (i,j) frame used for vpMESite
133 iP.set_ij(p_me.m_ifloat, p_me.m_jfloat);
134 p_me.m_alpha = computeTheta(iP);
135 *it = p_me;
136 }
137}
138
140{
141 // Two versions are available. If you change from one version to the other
142 // one, do not forget to adapt, for a correct display of an arc
143 // of ellipse, vpMeEllipse::display() below and
144 // vp_display_display_ellipse() in modules/core/src/display/vpDisplay_impl.h
145 // so that the two extremities of the arc are correctly shown.
146
147#ifdef VP_ME_ELLIPSE_REGULAR_SAMPLING
148 // Version that gives a regular angular sampling on the ellipse, so less
149 // points at its extremities
150 double co = cos(angle);
151 double si = sin(angle);
152 double coef = m_a * m_b / sqrt((m_b * m_b * co * co) + (m_a * m_a * si * si));
153 double u = co * coef;
154 double v = si * coef;
155 iP.set_u((uc + (m_ce * u)) - (m_se * v));
156 iP.set_v(vc + (m_se * u) + (m_ce * v));
157#elif defined(VP_ME_ELLIPSE_TWO_CONCENTRIC_CIRCLES)
158 // Version from "the two concentric circles" method that gives more points
159 // at the ellipse extremities for a regular angle sampling. It is better to
160 // display an ellipse, not necessarily to track it
161
162 // (u,v) are the coordinates on the canonical centered ellipse;
163 double u = m_a * cos(angle);
164 double v = m_b * sin(angle);
165 // a rotation of e and a translation by (uc,vc) are done
166 // to get the coordinates of the point on the shifted ellipse
167 iP.set_uv((m_uc + (m_ce * u)) - (m_se * v), m_vc + (m_se * u) + (m_ce * v));
168#endif
169}
170
172{
173 // Two versions are available. If you change from one version to the other
174 // one, do not forget to change also the reciprocal function
175 // computePointOnEllipse() just above. Adapt also the display; see comment
176 // at the beginning of computePointOnEllipse()
177
178#ifdef VP_ME_ELLIPSE_REGULAR_SAMPLING
179 // Regular angle sampling method
180 double du = pt.get_u() - uc;
181 double dv = pt.get_v() - vc;
182 double ang = atan2(dv, du) - m_e;
183 if (ang > M_PI) {
184 ang -= 2.0 * M_PI;
185 }
186 else if (ang < -M_PI) {
187 ang += 2.0 * M_PI;
188 }
189#ifdef VP_ME_ELLIPSE_TWO_CONCENTRIC_CIRCLES
190 // for the "two concentric circles method" starting from the previous one
191 // (just to remember the link between both methods:
192 // tan(theta_2cc) = a/b tan(theta_rs))
193
194 double co = cos(ang);
195 double si = sin(ang);
196 double coeff = 1.0 / sqrt((m_b * m_b * co * co) + (m_a * m_a * si * si));
197 si *= m_a * coeff;
198 co *= m_b * coeff;
199 ang = atan2(si, co);
200#endif
201#elif defined(VP_ME_ELLIPSE_TWO_CONCENTRIC_CIRCLES)
202 // For the "two concentric circles" method starting from scratch
203 double du = pt.get_u() - m_uc;
204 double dv = pt.get_v() - m_vc;
205 double co = ((du * m_ce) + (dv * m_se)) / m_a;
206 double si = ((-du * m_se) + (dv * m_ce)) / m_b;
207 double angle = atan2(si, co);
208#endif
209
210 return angle;
211}
212
214{
215 double num = m_n20 - m_n02;
216 double d = (num * num) + (4.0 * m_n11 * m_n11); // always >= 0
217 if (d <= std::numeric_limits<double>::epsilon()) {
218 m_e = 0.0; // case n20 = n02 and n11 = 0 : circle, e undefined
219 m_ce = 1.0;
220 m_se = 0.0;
221 m_a = (m_b = (2.0 * sqrt(m_n20))); // = sqrt(2.0*(n20+n02))
222 }
223 else { // real ellipse
224 m_e = atan2(2.0 * m_n11, num) / 2.0; // e in [-Pi/2 ; Pi/2]
225 m_ce = cos(m_e);
226 m_se = sin(m_e);
227
228 d = sqrt(d); // d in sqrt always >= 0
229 num = m_n20 + m_n02;
230 m_a = sqrt(2.0 * (num + d)); // term in sqrt always > 0
231 m_b = sqrt(2.0 * (num - d)); // term in sqrt always > 0
232 }
233}
234
236{
237 const unsigned int index_0 = 0;
238 const unsigned int index_1 = 1;
239 const unsigned int index_2 = 2;
240 const unsigned int index_3 = 3;
241 const unsigned int index_4 = 4;
242 const unsigned int index_5 = 5;
243 m_K[index_0] = m_n02;
244 m_K[index_1] = m_n20;
245 m_K[index_2] = -m_n11;
246 m_K[index_3] = (m_n11 * m_vc) - (m_n02 * m_uc);
247 m_K[index_4] = (m_n11 * m_uc) - (m_n20 * m_vc);
248 m_K[index_5] = (((m_n02 * m_uc * m_uc) + (m_n20 * m_vc * m_vc)) - (2.0 * m_n11 * m_uc * m_vc)) + (4.0 * ((m_n11 * m_n11) - (m_n20 * m_n02)));
249}
250
252{
253 m_n20 = 0.25 * ((m_a * m_a * m_ce * m_ce) + (m_b * m_b * m_se * m_se));
254 m_n11 = 0.25 * m_se * m_ce * ((m_a * m_a) - (m_b * m_b));
255 m_n02 = 0.25 * ((m_a * m_a * m_se * m_se) + (m_b * m_b * m_ce * m_ce));
256}
257
259{
260 const unsigned int index_0 = 0;
261 const unsigned int index_1 = 1;
262 const unsigned int index_2 = 2;
263 const unsigned int index_3 = 3;
264 const unsigned int index_4 = 4;
265 const unsigned int index_5 = 5;
266 // Equations below from Chaumette PhD and TRO 2004 paper
267 double num = (m_K[index_0] * m_K[index_1]) - (m_K[index_2] * m_K[index_2]); // > 0 for an ellipse
268 if (num <= 0) {
269 throw(vpTrackingException(vpTrackingException::fatalError, "The points do not belong to an ellipse! num: %f", num));
270 }
271
272 m_uc = ((m_K[index_2] * m_K[index_4]) - (m_K[index_1] * m_K[index_3])) / num;
273 m_vc = ((m_K[index_2] * m_K[index_3]) - (m_K[index_0] * m_K[index_4])) / num;
274 m_iPc.set_uv(m_uc, m_vc);
275
276 double d = (((m_K[index_0] * m_uc * m_uc) + (m_K[index_1] * m_vc * m_vc) + (2.0 * m_K[index_2] * m_uc * m_vc)) - m_K[index_5]) / (4.0 * num);
277 m_n20 = m_K[index_1] * d; // always > 0
278 m_n11 = -m_K[index_2] * d;
279 m_n02 = m_K[index_0] * d; // always > 0
280
282
283 // normalization so that K0 = n02, K1 = n20, etc (Eq (25) of TRO paper)
284 d = m_n02 / m_K[index_0]; // fabs(K[0]) > 0
285 unsigned int Ksize = m_K.size();
286 for (unsigned int i = 0; i < Ksize; ++i) {
287 m_K[i] *= d;
288 }
289}
290
292{
293 std::cout << "K :" << m_K.t() << std::endl;
294 std::cout << "xc = " << m_uc << ", yc = " << m_vc << std::endl;
295 std::cout << "n20 = " << m_n20 << ", n11 = " << m_n11 << ", n02 = " << m_n02 << std::endl;
296 std::cout << "A = " << m_a << ", B = " << m_b << ", E (dg) " << vpMath::deg(m_e) << std::endl;
297}
298
299void vpMeEllipse::sample(const vpImage<unsigned char> &I, bool doNotTrack)
300{
301 // Warning: similar code in vpMbtMeEllipse::sample()
302 if (!m_me) {
303 throw(vpTrackingException(vpTrackingException::fatalError, "Moving edges on ellipse not initialized"));
304 }
305 // Delete old lists
306 m_meList.clear();
307 m_angleList.clear();
308
309 int nbrows = static_cast<int>(I.getHeight());
310 int nbcols = static_cast<int>(I.getWidth());
311 // New version using distance for sampling
312 if (std::fabs(m_me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
313 std::cout << "Warning: In vpMeEllipse::sample() ";
314 std::cout << "function called with sample step = 0. We set it rather to 10 pixels";
315 // std::cout << "function called with sample step = 0, set to 10 dg";
316 m_me->setSampleStep(10.0);
317 }
318 // Perimeter of the ellipse using Ramanujan formula
319 double perim = M_PI * ((3.0 * (m_a + m_b)) - sqrt(((3.0 * m_a) + m_b) * (m_a + (3.0 * m_b))));
320 // Number of points for a complete ellipse
321 unsigned int nb_pt = static_cast<unsigned int>(floor(perim / m_me->getSampleStep()));
322 double incr = (2.0 * M_PI) / nb_pt;
323 // Compute of the expected density
324 if (!m_trackArc) { // number of points for a complete ellipse
325 m_expectedDensity = nb_pt;
326 }
327 else { // number of points for an arc of ellipse
328 m_expectedDensity = static_cast<unsigned int>(floor((perim / m_me->getSampleStep()) * ((m_alpha2 - m_alpha1) / (2.0 * M_PI))));
329 }
330
331 // Starting angle for sampling: new version to not start at 0
332 double ang = m_alpha1 + (incr / 2.0);
333
334 // sample positions
335 for (unsigned int i = 0; i < m_expectedDensity; ++i) {
336 vpImagePoint iP;
337 computePointOnEllipse(ang, iP);
338 // If point is in the image, add to the sample list
339 // Check done in (i,j) frame)
340 if (!outOfImage(iP, 0, nbrows, nbcols)) {
341 unsigned int is_uint = static_cast<unsigned int>(iP.get_i());
342 unsigned int js_uint = static_cast<unsigned int>(iP.get_j());
343 if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
344 const unsigned int crossSize = 5;
345 vpDisplay::displayCross(I, iP, crossSize, vpColor::red);
346
347 double theta = computeTheta(iP);
348 vpMeSite pix;
349 // (i,j) frame used for vpMeSite
350 pix.init(iP.get_i(), iP.get_j(), theta);
353 const double marginRatio = m_me->getThresholdMarginRatio();
354 double convolution = pix.convolution(I, m_me);
355 double contrastThreshold = fabs(convolution) * marginRatio;
356 pix.setContrastThreshold(contrastThreshold, *m_me);
357 m_meList.push_back(pix);
358 m_angleList.push_back(ang);
359 }
360 }
361 ang += incr;
362 }
363
364 if (!doNotTrack) {
366 }
367}
368
370{
371 if (!m_me) {
372 throw(vpTrackingException(vpTrackingException::fatalError, "Moving edges on ellipse tracking not initialized"));
373 }
374 unsigned int nb_pts_added = 0;
375 int nbrows = static_cast<int>(I.getHeight());
376 int nbcols = static_cast<int>(I.getWidth());
377 const unsigned int range_default = 2;
378
379 unsigned int memory_range = m_me->getRange();
380 m_me->setRange(range_default);
381
382 // Perimeter of the ellipse using Ramanujan formula
383 double perim = M_PI * ((3.0 * (m_a + m_b)) - sqrt(((3.0 * m_a) + m_b) * (m_a + (3.0 * m_b))));
384 // Number of points for a complete ellipse
385 unsigned int nb_pt = static_cast<unsigned int>(floor(perim / m_me->getSampleStep()));
386 double incr = (2.0 * M_PI) / nb_pt;
387
388 // Detect holes and try to complete them
389 // In this option, the sample step is used to complete the holes as much as possible
390 std::list<double>::iterator angleList = m_angleList.begin();
391 std::list<vpMeSite>::iterator meList = m_meList.begin();
392 const double marginRatio = m_me->getThresholdMarginRatio();
393 double ang = *angleList;
394 ++angleList;
395 ++meList;
396
397 while (meList != m_meList.end()) {
398 double nextang = *angleList;
399 if ((nextang - ang) > (2.0 * incr)) { // A hole exists
400 ang += incr; // next point to be checked
401 // adding only 1 point if hole of 1 point
402 while (ang < (nextang - incr)) {
403 vpImagePoint iP;
404 computePointOnEllipse(ang, iP);
405 if (!outOfImage(iP, 0, nbrows, nbcols)) {
406 unsigned int is_uint = static_cast<unsigned int>(iP.get_i());
407 unsigned int js_uint = static_cast<unsigned int>(iP.get_j());
408 if (inRoiMask(m_mask, is_uint, js_uint)) {
409 double theta = computeTheta(iP);
410 vpMeSite pix;
411 pix.init(iP.get_i(), iP.get_j(), theta);
414 double convolution = pix.convolution(I, m_me);
415 double contrastThreshold = fabs(convolution) * marginRatio;
416 pix.setContrastThreshold(contrastThreshold, *m_me);
417 pix.track(I, m_me, false);
418 if (pix.getState() == vpMeSite::NO_SUPPRESSION) { // good point
419 ++nb_pts_added;
420 iP.set_ij(pix.get_ifloat(), pix.get_jfloat());
421 double new_ang = computeAngleOnEllipse(iP);
422 if ((new_ang - ang) > M_PI) {
423 new_ang -= 2.0 * M_PI;
424 }
425 else if ((ang - new_ang) > M_PI) {
426 new_ang += 2.0 * M_PI;
427 }
428 m_meList.insert(meList, pix);
429 m_angleList.insert(angleList, new_ang);
430 }
431 }
432 }
433 ang += incr;
434 }
435 }
436 ang = nextang;
437 ++angleList;
438 ++meList;
439 }
440
441 // Add points in case two neighboring points are too far away
442 angleList = m_angleList.begin();
443 ang = *angleList;
444 meList = m_meList.begin();
445 vpMeSite pix1 = *meList;
446 ++angleList;
447 ++meList;
448 while (meList != m_meList.end()) {
449 double nextang = *angleList;
450 vpMeSite pix2 = *meList;
451 double dist = sqrt(((pix1.get_ifloat() - pix2.get_ifloat()) * (pix1.get_ifloat() - pix2.get_ifloat()))
452 + ((pix1.get_jfloat() - pix2.get_jfloat()) * (pix1.get_jfloat() - pix2.get_jfloat())));
453 // Only one point is added if two neighboring points are too far away
454 if (dist > (2.0 * m_me->getSampleStep())) {
455 ang = (nextang + ang) / 2.0; // point added at mid angle
456 vpImagePoint iP;
457 computePointOnEllipse(ang, iP);
458 if (!outOfImage(iP, 0, nbrows, nbcols)) {
459 unsigned int is_uint = static_cast<unsigned int>(iP.get_i());
460 unsigned int js_uint = static_cast<unsigned int>(iP.get_j());
461 if (inRoiMask(m_mask, is_uint, js_uint)) {
462 double theta = computeTheta(iP);
463 vpMeSite pix;
464 pix.init(iP.get_i(), iP.get_j(), theta);
467 double convolution = pix.convolution(I, m_me);
468 double contrastThreshold = fabs(convolution) * marginRatio;
469 pix.setContrastThreshold(contrastThreshold, *m_me);
470 pix.track(I, m_me, false);
471 if (pix.getState() == vpMeSite::NO_SUPPRESSION) { // good point
472 ++nb_pts_added;
473 iP.set_ij(pix.get_ifloat(), pix.get_jfloat());
474 double new_ang = computeAngleOnEllipse(iP);
475 if ((new_ang - ang) > M_PI) {
476 new_ang -= 2.0 * M_PI;
477 }
478 else if ((ang - new_ang) > M_PI) {
479 new_ang += 2.0 * M_PI;
480 }
481 m_meList.insert(meList, pix);
482 m_angleList.insert(angleList, new_ang);
483 }
484 }
485 }
486 }
487 ang = nextang;
488 pix1 = pix2;
489 ++angleList;
490 ++meList;
491 }
492
493 // Try to fill the first extremity: from alpha_min - incr to alpha1 + incr/2
494 meList = m_meList.begin();
495 pix1 = *meList;
496 unsigned int nbpts = 0;
497 // Add - incr/2.0 to avoid being too close to 0
498 if ((m_alphamin - m_alpha1 - (incr / 2.0)) > 0.0) {
499 nbpts = static_cast<unsigned int>(floor((m_alphamin - m_alpha1 - (incr / 2.0)) / incr));
500 }
501 ang = m_alphamin - incr;
502 for (unsigned int i = 0; i < nbpts; ++i) {
503 vpImagePoint iP;
504 computePointOnEllipse(ang, iP);
505 if (!outOfImage(iP, 0, nbrows, nbcols)) {
506 unsigned int is_uint = static_cast<unsigned int>(iP.get_i());
507 unsigned int js_uint = static_cast<unsigned int>(iP.get_j());
508 if (inRoiMask(m_mask, is_uint, js_uint)) {
509 double theta = computeTheta(iP);
510 vpMeSite pix;
511 pix.init(iP.get_i(), iP.get_j(), theta);
514 // --comment: pix dot setContrastThreshold of pix1 dot getContrastThreshold() comma *m_me
515 double convolution = pix.convolution(I, m_me);
516 double contrastThreshold = fabs(convolution) * marginRatio;
517 pix.setContrastThreshold(contrastThreshold, *m_me);
518 pix.track(I, m_me, false);
519 if (pix.getState() == vpMeSite::NO_SUPPRESSION) {
520 ++nb_pts_added;
521 iP.set_ij(pix.get_ifloat(), pix.get_jfloat());
522 double new_ang = computeAngleOnEllipse(iP);
523 if ((new_ang - ang) > M_PI) {
524 new_ang -= 2.0 * M_PI;
525 }
526 else if ((ang - new_ang) > M_PI) {
527 new_ang += 2.0 * M_PI;
528 }
529 m_meList.push_front(pix);
530 m_angleList.push_front(new_ang);
531 }
532 }
533 }
534 ang -= incr;
535 }
536
537 // Try to fill the second extremity: from alphamax + incr to alpha2 - incr/2
538 pix1 = m_meList.back();
539 nbpts = 0;
540 if ((m_alpha2 - (incr / 2.0) - m_alphamax) > 0.0) {
541 nbpts = static_cast<unsigned int>(floor((m_alpha2 - (incr / 2.0) - m_alphamax) / incr));
542 }
543
544 ang = m_alphamax + incr;
545 for (unsigned int i = 0; i < nbpts; ++i) {
546 vpImagePoint iP;
547 computePointOnEllipse(ang, iP);
548 if (!outOfImage(iP, 0, nbrows, nbcols)) {
549 unsigned int is_uint = static_cast<unsigned int>(iP.get_i());
550 unsigned int js_uint = static_cast<unsigned int>(iP.get_j());
551 if (inRoiMask(m_mask, is_uint, js_uint)) {
552 double theta = computeTheta(iP);
553 vpMeSite pix;
554 pix.init(iP.get_i(), iP.get_j(), theta);
557 double convolution = pix.convolution(I, m_me);
558 double contrastThreshold = fabs(convolution) * marginRatio;
559 pix.setContrastThreshold(contrastThreshold, *m_me);
560 pix.track(I, m_me, false);
561 if (pix.getState() == vpMeSite::NO_SUPPRESSION) {
562 ++nb_pts_added;
563 iP.set_ij(pix.get_ifloat(), pix.get_jfloat());
564 double new_ang = computeAngleOnEllipse(iP);
565 if ((new_ang - ang) > M_PI) {
566 new_ang -= 2.0 * M_PI;
567 }
568 else if ((ang - new_ang) > M_PI) {
569 new_ang += 2.0 * M_PI;
570 }
571 m_meList.push_back(pix);
572 m_angleList.push_back(new_ang);
573 }
574 }
575 }
576 ang += incr;
577 }
578 m_me->setRange(memory_range);
579
580 if (m_meList.size() != m_angleList.size()) {
581 // Should never occur
582 throw(vpTrackingException(vpTrackingException::fatalError, "Lists are not coherent in vpMeEllipse::plugHoles(): nb MEs %ld, nb ang %ld",
583 m_meList.size(), m_angleList.size()));
584 }
585
586 return nb_pts_added;
587}
588
589void vpMeEllipse::display(const vpImage<unsigned char> &I, const vpColor &col, unsigned int thickness)
590{
592}
593
594void vpMeEllipse::display(const vpImage<vpRGBa> &I, const vpColor &col, unsigned int thickness)
595{
597}
598
599void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, bool trackCircle, bool trackArc)
600{
601 unsigned int n = 5; // by default, 5 points for an ellipse
602 const unsigned int nForCircle = 3;
603 m_trackCircle = trackCircle;
604 if (trackCircle) {
605 n = nForCircle;
606 }
607 std::vector<vpImagePoint> iP(n);
608 m_trackArc = trackArc;
609
611
612 if (m_trackCircle) {
613 if (m_trackArc) {
614 std::cout << "First and third points specify the extremities of the arc of circle (clockwise)" << std::endl;
615 }
616 for (unsigned int k = 0; k < n; ++k) {
617 std::cout << "Click point " << (k + 1) << "/" << n << " on the circle " << std::endl;
618 vpDisplay::getClick(I, iP[k], true);
619 const unsigned int crossSize = 10;
620 vpDisplay::displayCross(I, iP[k], crossSize, vpColor::red);
622 std::cout << iP[k] << std::endl;
623 }
624 }
625 else {
626 if (m_trackArc) {
627 std::cout << "First and fifth points specify the extremities of the arc of ellipse (clockwise)" << std::endl;
628 }
629 for (unsigned int k = 0; k < n; ++k) {
630 std::cout << "Click point " << (k + 1) << "/" << n << " on the ellipse " << std::endl;
631 vpDisplay::getClick(I, iP[k], true);
632 const unsigned int crossSize = 10;
633 vpDisplay::displayCross(I, iP[k], crossSize, vpColor::red);
635 std::cout << iP[k] << std::endl;
636 }
637 }
638 initTracking(I, iP, trackCircle, trackArc);
639}
640
641void vpMeEllipse::initTracking(const vpImage<unsigned char> &I, const std::vector<vpImagePoint> &iP,
642 bool trackCircle, bool trackArc)
643{
644 m_trackArc = trackArc;
645 m_trackCircle = trackCircle;
646 // useful for sample(I):
647 leastSquare(I, iP);
648 if (trackArc) {
649 // useful for track(I):
650 m_iP1 = iP.front();
651 m_iP2 = iP.back();
652 // useful for sample(I):
655 if ((m_alpha2 <= m_alpha1) || (std::fabs(m_alpha2 - m_alpha1) < m_arcEpsilon)) {
656 m_alpha2 += 2.0 * M_PI;
657 }
658 }
659 else {
660 m_alpha1 = 0.0;
661 m_alpha2 = 2.0 * M_PI;
662 // useful for track(I):
663 vpImagePoint ip;
665 m_iP1 = (m_iP2 = ip);
666 }
667
668 sample(I);
669 track(I);
672}
673
675 const vpImagePoint *pt2, bool trackCircle)
676{
677 const unsigned int index_0 = 0;
678 const unsigned int index_1 = 1;
679 const unsigned int index_2 = 2;
680 const unsigned int index_3 = 3;
681 const unsigned int index_4 = 4;
682
683 m_trackCircle = trackCircle;
684 if ((pt1 != nullptr) && (pt2 != nullptr)) {
685 m_trackArc = true;
686 }
687 // useful for sample(I) : uc, vc, a, b, e, Ki, alpha1, alpha2
688 m_uc = param[index_0];
689 m_vc = param[index_1];
690 m_n20 = param[index_2];
691 m_n11 = param[index_3];
692 m_n02 = param[index_4];
695
696 if (m_trackArc && pt1 && pt2) {
699 if ((m_alpha2 <= m_alpha1) || (std::fabs(m_alpha2 - m_alpha1) < m_arcEpsilon)) {
700 m_alpha2 += 2.0 * M_PI;
701 }
702 // useful for track(I)
703 m_iP1 = *pt1;
704 m_iP2 = *pt2;
705 }
706 else {
707 m_alpha1 = 0.0;
708 m_alpha2 = 2.0 * M_PI;
709 // useful for track(I)
710 vpImagePoint ip;
712 m_iP1 = (m_iP2 = ip);
713 }
714 // useful for display(I) so useless if no display before track(I)
715 m_iPc.set_uv(m_uc, m_vc);
716
717 sample(I);
718 track(I);
721}
722
729{
731
732 // recompute alpha1 and alpha2 in case they have been changed by setEndPoints()
733 if (m_trackArc) {
736 if ((m_alpha2 <= m_alpha1) || (std::fabs(m_alpha2 - m_alpha1) < m_arcEpsilon)) {
737 m_alpha2 += 2.0 * M_PI;
738 }
739 }
740 // Compute the ellipse parameters from the tracked points, manage the lists,
741 // and update the expected density (
743
744 if (plugHoles(I) > 0) {
745 m_numberOfGoodPoints = leastSquareRobust(I); // if new points have been added, recompute the ellipse parameters and manage again the lists
746 }
747
748 const unsigned int minNbGoodPoints = 5;
749 if (m_numberOfGoodPoints <= minNbGoodPoints) {
750 sample(I);
753
754 // Stop in case of failure after resample
755 if (m_numberOfGoodPoints <= minNbGoodPoints) {
756 throw(vpTrackingException(vpTrackingException::notEnoughPointError, "Impossible to track the ellipse, not enough features"));
757 }
758 }
759
760 // remet a jour l'angle delta pour chaque vpMeSite de la liste
761 updateTheta();
762 // not in getParameters since computed only once for each image
763 m_m00 = M_PI * m_a * m_b;
764
765 // Useful only for tracking an arc of ellipse, but done to give them a value
768}
769
770#ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
771
796void vpMeEllipse::display(const vpImage<unsigned char> &I, const vpImagePoint &center, const double &A,
797 const double &B, const double &E, const double &smallalpha, const double &highalpha,
798 const vpColor &color, unsigned int thickness)
799{
800 vpMeEllipse::displayEllipse(I, center, A, B, E, smallalpha, highalpha, color, thickness);
801}
802
830void vpMeEllipse::display(const vpImage<vpRGBa> &I, const vpImagePoint &center, const double &A, const double &B,
831 const double &E, const double &smallalpha, const double &highalpha,
832 const vpColor &color, unsigned int thickness)
833{
834 vpMeEllipse::displayEllipse(I, center, A, B, E, smallalpha, highalpha, color, thickness);
835}
836#endif // Deprecated
837
838
839void vpMeEllipse::displayEllipse(const vpImage<unsigned char> &I, const vpImagePoint &center, const double &A,
840 const double &B, const double &E, const double &smallalpha, const double &highalpha,
841 const vpColor &color, unsigned int thickness)
842{
843 vpDisplay::displayEllipse(I, center, A, B, E, smallalpha, highalpha, false, color, thickness, true, true);
844}
845
846void vpMeEllipse::displayEllipse(const vpImage<vpRGBa> &I, const vpImagePoint &center, const double &A, const double &B,
847 const double &E, const double &smallalpha, const double &highalpha,
848 const vpColor &color, unsigned int thickness)
849{
850 vpDisplay::displayEllipse(I, center, A, B, E, smallalpha, highalpha, false, color, thickness, true, true);
851}
852
853END_VISP_NAMESPACE
Implementation of column vector and the associated operations.
Class to define RGB colors available for display functionalities.
Definition vpColor.h:157
static const vpColor red
Definition vpColor.h:198
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
static void displayEllipse(const vpImage< unsigned char > &I, const vpImagePoint &center, const double &coef1, const double &coef2, const double &coef3, bool use_normalized_centered_moments, const vpColor &color, unsigned int thickness=1, bool display_center=false, bool display_arc=false)
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)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
double get_j() const
void set_ij(double ii, double jj)
double get_u() const
void set_u(double u)
void set_uv(double u, double v)
void set_v(double v)
double get_i() const
double get_v() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
static double deg(double rad)
Definition vpMath.h:119
double m_n20
Second order centered and normalized moments .
double m_arcEpsilon
Epsilon value used to check if arc angles are the same.
void computePointOnEllipse(const double angle, vpImagePoint &iP)
void display(const vpImage< unsigned char > &I, const vpColor &col, unsigned int thickness=1)
double m_alpha2
vpImagePoint m_iPc
The coordinates of the ellipse center.
double m_vc
Value of v coordinate of iPc.
unsigned int m_numberOfGoodPoints
Number of correct points tracked along the ellipse.
void computeKiFromNij()
unsigned int plugHoles(const vpImage< unsigned char > &I)
void computeNijFromAbe()
vpMeEllipse & operator=(const vpMeEllipse &me_ellipse)
void getParameters()
void printParameters() const
double m_uc
Value of u coordinate of iPc.
bool m_trackCircle
Track a circle (true) or an ellipse (false).
void computeAbeFromNij()
vpColVector m_K
double m_m00
Ellipse area.
double m_alphamin
std::list< double > m_angleList
Stores the value in increasing order of the angle on the ellipse for each vpMeSite.
bool m_trackArc
Track an arc of ellipse/circle (true) or a complete one (false).
void initTracking(const vpImage< unsigned char > &I, bool trackCircle=false, bool trackArc=false)
double m_a
is the semi major axis of the ellipse.
unsigned int leastSquareRobust(const vpImage< unsigned char > &I)
double m_alpha1
void leastSquare(const vpImage< unsigned char > &I, const std::vector< vpImagePoint > &iP)
double m_b
is the semi minor axis of the ellipse.
double m_alphamax
double computeTheta(const vpImagePoint &iP) const
double m_se
Value of sin(e).
double m_n02
Second order centered and normalized moments .
unsigned int m_expectedDensity
Expected number of points to track along the ellipse.
void updateTheta()
vpImagePoint m_iP2
double m_n11
Second order centered and normalized moments .
virtual void sample(const vpImage< unsigned char > &I, bool doNotTrack=false) VP_OVERRIDE
vpImagePoint m_iP1
void track(const vpImage< unsigned char > &I)
virtual ~vpMeEllipse() VP_OVERRIDE
double m_ce
Value of cos(e).
static void displayEllipse(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=vpColor::green, unsigned int thickness=1)
double m_thresholdWeight
Threshold on the weights for the robust least square.
double computeAngleOnEllipse(const vpImagePoint &pt) const
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 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
double m_alpha
Angle of tangent at site.
Definition vpMeSite.h:116
vpMeSiteState getState() const
Definition vpMeSite.h:306
double get_ifloat() const
Definition vpMeSite.h:212
double m_jfloat
Subpixel coordinates along j of a site.
Definition vpMeSite.h:112
double get_jfloat() const
Definition vpMeSite.h:218
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 setState(const vpMeSiteState &flag)
Definition vpMeSite.h:296
void initTracking(const vpImage< unsigned char > &I)
const vpImage< bool > * m_mask
Mask used to disable tracking on a part of image.
vpMeSite::vpMeSiteDisplayType m_selectDisplay
Moving-edges display type.
void track(const vpImage< unsigned char > &I)
vpMe * m_me
Moving edges initialisation parameters.
static bool inRoiMask(const vpImage< bool > *mask, unsigned int i, unsigned int j)
void display(const vpImage< unsigned char > &I)
bool outOfImage(int i, int j, int border, int nrows, int ncols)
const vpImage< bool > * m_maskCandidates
Mask used to determine candidate points for initialization in an image.
std::list< vpMeSite > m_meList
static bool inMeMaskCandidates(const vpImage< bool > *meMaskCandidates, unsigned int i, unsigned int j)
Error that can be emitted by the vpTracker class and its derivatives.
@ notEnoughPointError
Not enough point to track.
@ fatalError
Tracker fatal error.