Visual Servoing Platform  version 3.1.0
vpMbtMeEllipse.cpp
1 /****************************************************************************
2  *
3  * This file is part of the ViSP software.
4  * Copyright (C) 2005 - 2017 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 #ifndef DOXYGEN_SHOULD_SKIP_THIS
40 
41 #include <visp3/mbt/vpMbtMeEllipse.h>
42 
43 #include <visp3/core/vpDebug.h>
44 #include <visp3/core/vpImagePoint.h>
45 #include <visp3/core/vpRobust.h>
46 #include <visp3/core/vpTrackingException.h>
47 #include <visp3/me/vpMe.h>
48 
49 #include <algorithm> // (std::min)
50 #include <cmath> // std::fabs
51 #include <limits> // numeric_limits
52 
56 vpMbtMeEllipse::vpMbtMeEllipse()
57  : iPc(), a(0.), b(0.), e(0.), ce(0.), se(0.), mu11(0.), mu20(0.), mu02(0.), thresholdWeight(0.), expecteddensity(0.)
58 {
59 }
60 
64 vpMbtMeEllipse::vpMbtMeEllipse(const vpMbtMeEllipse &meellipse)
65  : vpMeTracker(meellipse), iPc(meellipse.iPc), a(0.), b(0.), e(0.), ce(0.), se(0.), mu11(0.), mu20(0.), mu02(0.),
66  thresholdWeight(0.), expecteddensity(0.)
67 {
68  a = meellipse.a;
69  b = meellipse.b;
70  e = meellipse.e;
71 
72  ce = meellipse.ce;
73  se = meellipse.se;
74 
75  mu11 = meellipse.mu11;
76  mu20 = meellipse.mu20;
77  mu02 = meellipse.mu02;
78 
79  expecteddensity = meellipse.expecteddensity;
80 }
81 
85 vpMbtMeEllipse::~vpMbtMeEllipse() { list.clear(); }
86 
97 void vpMbtMeEllipse::computeProjectionError(const vpImage<unsigned char> &_I, double &_sumErrorRad,
98  unsigned int &_nbFeatures)
99 {
100  _sumErrorRad = 0;
101  _nbFeatures = 0;
102 
103  // vpMatrix filterX(3,3);
104  // filterX[0][0] = -1;
105  // filterX[1][0] = -2;
106  // filterX[2][0] = -1;
107 
108  // filterX[0][1] = 0;
109  // filterX[1][1] = 0;
110  // filterX[2][1] = 0;
111 
112  // filterX[0][2] = 1;
113  // filterX[1][2] = 2;
114  // filterX[2][2] = 1;
115 
116  // vpMatrix filterY(3,3);
117  // filterY[0][0] = -1;
118  // filterY[0][1] = -2;
119  // filterY[0][2] = -1;
120 
121  // filterY[1][0] = 0;
122  // filterY[1][1] = 0;
123  // filterY[1][2] = 0;
124 
125  // filterY[2][0] = 1;
126  // filterY[2][1] = 2;
127  // filterY[2][2] = 1;
128 
129  vpMatrix filterX(5, 5);
130  filterX[0][0] = -1;
131  filterX[1][0] = -4;
132  filterX[2][0] = -6;
133  filterX[3][0] = -4;
134  filterX[4][0] = -1;
135 
136  filterX[0][1] = -2;
137  filterX[1][1] = -8;
138  filterX[2][1] = -12;
139  filterX[3][1] = -8;
140  filterX[4][1] = -2;
141 
142  filterX[0][2] = 0;
143  filterX[1][2] = 0;
144  filterX[2][2] = 0;
145  filterX[3][2] = 0;
146  filterX[4][2] = 0;
147 
148  filterX[0][3] = 2;
149  filterX[1][3] = 8;
150  filterX[2][3] = 12;
151  filterX[3][3] = 8;
152  filterX[4][3] = 2;
153 
154  filterX[0][4] = 1;
155  filterX[1][4] = 4;
156  filterX[2][4] = 6;
157  filterX[3][4] = 4;
158  filterX[4][4] = 1;
159 
160  vpMatrix filterY(5, 5);
161  filterY[0][0] = -1;
162  filterY[0][1] = -4;
163  filterY[0][2] = -6;
164  filterY[0][3] = -4;
165  filterY[0][4] = -1;
166 
167  filterY[1][0] = -2;
168  filterY[1][1] = -8;
169  filterY[1][2] = -12;
170  filterY[1][3] = -8;
171  filterY[1][4] = -2;
172 
173  filterY[2][0] = 0;
174  filterY[2][1] = 0;
175  filterY[2][2] = 0;
176  filterY[2][3] = 0;
177  filterY[2][4] = 0;
178 
179  filterY[3][0] = 2;
180  filterY[3][1] = 8;
181  filterY[3][2] = 12;
182  filterY[3][3] = 8;
183  filterY[3][4] = 2;
184 
185  filterY[4][0] = 1;
186  filterY[4][1] = 4;
187  filterY[4][2] = 6;
188  filterY[4][3] = 4;
189  filterY[4][4] = 1;
190 
191  double offset = std::floor(filterX.getRows() / 2.0f);
192  // std::cout << "offset=" << offset << std::endl;
193  int height = (int)_I.getHeight();
194  int width = (int)_I.getWidth();
195 
196  for (std::list<vpMeSite>::iterator it = list.begin(); it != list.end(); ++it) {
197  double iSite = it->ifloat;
198  double jSite = it->jfloat;
199 
200  if (!outOfImage(vpMath::round(iSite), vpMath::round(jSite), 0, height,
201  width)) { // Check if necessary
202  // The tangent angle to the ellipse at a site
203  double theta = atan((-mu02 * jSite + mu02 * iPc.get_j() + mu11 * iSite - mu11 * iPc.get_i()) /
204  (mu20 * iSite - mu11 * jSite + mu11 * iPc.get_j() - mu20 * iPc.get_i())) -
205  M_PI / 2;
206 
207  double deltaNormalized = theta;
208  while (deltaNormalized < 0)
209  deltaNormalized += M_PI;
210  while (deltaNormalized > M_PI)
211  deltaNormalized -= M_PI;
212 
213  vpColVector vecSite(2);
214  vecSite[0] = cos(deltaNormalized);
215  vecSite[1] = sin(deltaNormalized);
216  vecSite.normalize();
217 
218  double gradientX = 0;
219  double gradientY = 0;
220 
221  for (unsigned int i = 0; i < filterX.getRows(); i++) {
222  double iImg = iSite + (i - offset);
223  for (unsigned int j = 0; j < filterX.getCols(); j++) {
224  double jImg = jSite + (j - offset);
225 
226  if (iImg < 0)
227  iImg = 0.0;
228  if (jImg < 0)
229  jImg = 0.0;
230 
231  if (iImg > _I.getHeight() - 1)
232  iImg = _I.getHeight() - 1;
233  if (jImg > _I.getWidth() - 1)
234  jImg = _I.getWidth() - 1;
235 
236  gradientX += filterX[i][j] * _I((unsigned int)iImg, (unsigned int)jImg);
237  }
238  }
239 
240  for (unsigned int i = 0; i < filterY.getRows(); i++) {
241  double iImg = iSite + (i - offset);
242  for (unsigned int j = 0; j < filterY.getCols(); j++) {
243  double jImg = jSite + (j - offset);
244 
245  if (iImg < 0)
246  iImg = 0.0;
247  if (jImg < 0)
248  jImg = 0.0;
249 
250  if (iImg > _I.getHeight() - 1)
251  iImg = _I.getHeight() - 1;
252  if (jImg > _I.getWidth() - 1)
253  jImg = _I.getWidth() - 1;
254 
255  gradientY += filterY[i][j] * _I((unsigned int)iImg, (unsigned int)jImg);
256  }
257  }
258 
259  double angle = atan2(gradientY, gradientX);
260  while (angle < 0)
261  angle += M_PI;
262  while (angle > M_PI)
263  angle -= M_PI;
264 
265  // if(std::fabs(deltaNormalized-angle) > M_PI / 2)
266  // {
267  // sumErrorRad += sqrt(vpMath::sqr(deltaNormalized-angle)) - M_PI
268  // / 2;
269  // } else {
270  // sumErrorRad += sqrt(vpMath::sqr(deltaNormalized-angle));
271  // }
272 
273  // double angle1 = sqrt(vpMath::sqr(deltaNormalized-angle));
274  // double angle2 = sqrt(vpMath::sqr(deltaNormalized-
275  // (angle-M_PI)));
276 
277  vpColVector vecGrad(2);
278  vecGrad[0] = cos(angle);
279  vecGrad[1] = sin(angle);
280  vecGrad.normalize();
281 
282  double angle1 = acos(vecSite * vecGrad);
283  double angle2 = acos(vecSite * (-vecGrad));
284 
285  _sumErrorRad += (std::min)(angle1, angle2);
286 
287  _nbFeatures++;
288  }
289  }
290 }
291 
303 void vpMbtMeEllipse::sample(const vpImage<unsigned char> &I)
304 {
305  if (!me) {
306  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
307  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
308  }
309 
310  int height = (int)I.getHeight();
311  int width = (int)I.getWidth();
312 
313  // if (me->getSampleStep()==0)
314  if (std::fabs(me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
315  std::cout << "In vpMbtMeEllipse::sample: ";
316  std::cout << "function called with sample step = 0";
317  // return fatalError;
318  }
319 
320  // Approximation of the circumference of an ellipse:
321  // [Ramanujan, S., "Modular Equations and Approximations to ,"
322  // Quart. J. Pure. Appl. Math., vol. 45 (1913-1914), pp. 350-372]
323  double t = (a - b) / (a + b);
324  double circumference = M_PI * (a + b) * (1 + 3 * vpMath::sqr(t) / (10 + sqrt(4 - 3 * vpMath::sqr(t))));
325  int nb_points_to_track = (int)(circumference / me->getSampleStep());
326  double incr = 2 * M_PI / nb_points_to_track;
327 
328  expecteddensity = 0; // nb_points_to_track;
329 
330  // Delete old list
331  list.clear();
332 
333  // sample positions
334  double k = 0;
335  for (int pt = 0; pt < nb_points_to_track; pt++) {
336  double j = a * cos(k); // equation of an ellipse
337  double i = b * sin(k); // equation of an ellipse
338 
339  double iP_j = iPc.get_j() + ce * j - se * i;
340  double iP_i = iPc.get_i() + se * j + ce * i;
341 
342  // vpColor col = vpColor::red;
343  // vpDisplay::displayCross(I, vpImagePoint(iP_i, iP_j), 5, col) ; //debug
344  // only
345 
346  // If point is in the image, add to the sample list
347  if (!outOfImage(vpMath::round(iP_i), vpMath::round(iP_j), 0, height, width)) {
348  // The tangent angle to the ellipse at a site
349  double theta = atan((-mu02 * iP_j + mu02 * iPc.get_j() + mu11 * iP_i - mu11 * iPc.get_i()) /
350  (mu20 * iP_i - mu11 * iP_j + mu11 * iPc.get_j() - mu20 * iPc.get_i())) -
351  M_PI / 2;
352 
353  vpMeSite pix;
354  pix.init((int)iP_i, (int)iP_j, theta);
355  pix.setDisplay(selectDisplay);
357 
358  list.push_back(pix);
359  expecteddensity++;
360  }
361  k += incr;
362  }
363 
365 }
366 
382 void vpMbtMeEllipse::reSample(const vpImage<unsigned char> &I)
383 {
384  if (!me) {
385  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
386  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
387  }
388 
389  unsigned int n = numberOfSignal();
390  if ((double)n < 0.9 * expecteddensity) {
391  sample(I);
393  }
394 }
395 
401 void vpMbtMeEllipse::updateTheta()
402 {
403  vpMeSite p_me;
404  for (std::list<vpMeSite>::iterator it = list.begin(); it != list.end(); ++it) {
405  p_me = *it;
406  vpImagePoint iP;
407  iP.set_i(p_me.ifloat);
408  iP.set_j(p_me.jfloat);
409 
410  // The tangent angle to the ellipse at a site
411  double theta = atan((-mu02 * p_me.jfloat + mu02 * iPc.get_j() + mu11 * p_me.ifloat - mu11 * iPc.get_i()) /
412  (mu20 * p_me.ifloat - mu11 * p_me.jfloat + mu11 * iPc.get_j() - mu20 * iPc.get_i())) -
413  M_PI / 2;
414 
415  p_me.alpha = theta;
416  *it = p_me;
417  }
418 }
419 
424 void vpMbtMeEllipse::suppressPoints()
425 {
426  // Loop through list of sites to track
427  for (std::list<vpMeSite>::iterator itList = list.begin(); itList != list.end();) {
428  vpMeSite s = *itList; // current reference pixel
430  itList = list.erase(itList);
431  else
432  ++itList;
433  }
434 }
435 
445 void vpMbtMeEllipse::display(const vpImage<unsigned char> &I, vpColor col)
446 {
447  vpDisplay::displayEllipse(I, iPc, mu20, mu11, mu02, true, col);
448 }
449 
450 void vpMbtMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &ic, double mu20_p, double mu11_p,
451  double mu02_p)
452 {
453  iPc = ic;
454  mu20 = mu20_p;
455  mu11 = mu11_p;
456  mu02 = mu02_p;
457 
458  if (std::fabs(mu11_p) > std::numeric_limits<double>::epsilon()) {
459 
460  double val_p = sqrt(vpMath::sqr(mu20_p - mu02_p) + 4 * vpMath::sqr(mu11_p));
461  a = sqrt((mu20_p + mu02_p + val_p) / 2);
462  b = sqrt((mu20_p + mu02_p - val_p) / 2);
463 
464  e = (mu02_p - mu20_p + val_p) / (2 * mu11_p);
465  } else {
466  a = sqrt(mu20_p);
467  b = sqrt(mu02_p);
468  e = 0.;
469  }
470 
471  e = atan(e);
472 
473  ce = cos(e);
474  se = sin(e);
475 
476  sample(I);
477 
479 
480  try {
481  track(I);
482  } catch (vpException &exception) {
483  throw(exception);
484  }
485 }
486 
492 void vpMbtMeEllipse::track(const vpImage<unsigned char> &I)
493 {
494  try {
496  } catch (vpException &exception) {
497  throw(exception);
498  }
499 }
500 
501 void vpMbtMeEllipse::updateParameters(const vpImage<unsigned char> &I, const vpImagePoint &ic, double mu20_p,
502  double mu11_p, double mu02_p)
503 {
504  iPc = ic;
505  mu20 = mu20_p;
506  mu11 = mu11_p;
507  mu02 = mu02_p;
508 
509  if (std::fabs(mu11_p) > std::numeric_limits<double>::epsilon()) {
510 
511  double val_p = sqrt(vpMath::sqr(mu20_p - mu02_p) + 4 * vpMath::sqr(mu11_p));
512  a = sqrt((mu20_p + mu02_p + val_p) / 2);
513  b = sqrt((mu20_p + mu02_p - val_p) / 2);
514 
515  e = (mu02_p - mu20_p + val_p) / (2 * mu11_p);
516  } else {
517  a = sqrt(mu20_p);
518  b = sqrt(mu02_p);
519  e = 0.;
520  }
521 
522  e = atan(e);
523 
524  ce = cos(e);
525  se = sin(e);
526 
527  suppressPoints();
528  reSample(I);
529 
530  // remet a jour l'angle delta pour chaque point de la liste
531  updateTheta();
532 }
533 
534 #endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:104
static void displayEllipse(const vpImage< unsigned char > &I, const vpImagePoint &center, const double &coef1, const double &coef2, const double &coef3, bool use_centered_moments, const vpColor &color, unsigned int thickness=1)
vpMeSiteState getState() const
Definition: vpMeSite.h:188
void init()
Definition: vpMeSite.cpp:66
double jfloat
Definition: vpMeSite.h:88
Performs search in a given direction(normal) for a given distance(pixels) for a given &#39;site&#39;...
Definition: vpMeSite.h:71
Class to define colors available for display functionnalities.
Definition: vpColor.h:120
double alpha
Definition: vpMeSite.h:92
error that can be emited by ViSP classes.
Definition: vpException.h:71
static int round(const double x)
Definition: vpMath.h:235
double ifloat
Definition: vpMeSite.h:88
void set_i(const double ii)
Definition: vpImagePoint.h:167
Error that can be emited by the vpTracker class and its derivates.
double getSampleStep() const
Definition: vpMe.h:285
static double sqr(double x)
Definition: vpMath.h:108
void setDisplay(vpMeSiteDisplayType select)
Definition: vpMeSite.h:137
void track(const vpImage< unsigned char > &I)
Track sampled pixels.
Contains abstract elements for a Distance to Feature type feature.
Definition: vpMeTracker.h:65
void setState(const vpMeSiteState &flag)
Definition: vpMeSite.h:174
void set_j(const double jj)
Definition: vpImagePoint.h:178
#define vpDERROR_TRACE
Definition: vpDebug.h:464
unsigned int getHeight() const
Definition: vpImage.h:178
Implementation of column vector and the associated operations.
Definition: vpColVector.h:72
void initTracking(const vpImage< unsigned char > &I)
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:88
unsigned int getWidth() const
Definition: vpImage.h:229