Visual Servoing Platform  version 3.6.1 under development (2024-04-20)
vpMbtMeEllipse.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 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 https://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 *****************************************************************************/
35 
36 #ifndef DOXYGEN_SHOULD_SKIP_THIS
37 
38 #include <visp3/mbt/vpMbtMeEllipse.h>
39 
40 #include <visp3/core/vpDebug.h>
41 #include <visp3/core/vpImagePoint.h>
42 #include <visp3/core/vpRobust.h>
43 #include <visp3/core/vpTrackingException.h>
44 #include <visp3/me/vpMe.h>
45 
46 #include <algorithm> // (std::min)
47 #include <cmath> // std::fabs
48 #include <limits> // numeric_limits
49 
53 vpMbtMeEllipse::vpMbtMeEllipse() : vpMeEllipse() { }
54 
58 vpMbtMeEllipse::vpMbtMeEllipse(const vpMbtMeEllipse &me_ellipse) : vpMeEllipse(me_ellipse) { }
59 
75 void vpMbtMeEllipse::computeProjectionError(const vpImage<unsigned char> &I, double &sumErrorRad,
76  unsigned int &nbFeatures, const vpMatrix &SobelX, const vpMatrix &SobelY,
77  bool display, unsigned int length, unsigned int thickness)
78 {
79  sumErrorRad = 0;
80  nbFeatures = 0;
81 
82  double offset = static_cast<double>(std::floor(SobelX.getRows() / 2.0f));
83  int height = static_cast<int>(I.getHeight());
84  int width = static_cast<int>(I.getWidth());
85 
86  double max_iImg = height - 1.;
87  double max_jImg = width - 1.;
88 
89  vpColVector vecSite(2);
90  vpColVector vecGrad(2);
91 
92  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
93  double iSite = it->m_ifloat;
94  double jSite = it->m_jfloat;
95 
96  if (!outOfImage(vpMath::round(iSite), vpMath::round(jSite), 0, height, width)) { // Check if necessary
97  // The tangent angle to the ellipse at a site
98  double theta = computeTheta(vpImagePoint(iSite, jSite));
99 
100  vecSite[0] = cos(theta);
101  vecSite[1] = sin(theta);
102  vecSite.normalize();
103 
104  double gradientX = 0;
105  double gradientY = 0;
106 
107  for (unsigned int i = 0; i < SobelX.getRows(); i++) {
108  double iImg = iSite + (i - offset);
109  for (unsigned int j = 0; j < SobelX.getCols(); j++) {
110  double jImg = jSite + (j - offset);
111 
112  if (iImg < 0)
113  iImg = 0.0;
114  if (jImg < 0)
115  jImg = 0.0;
116 
117  if (iImg > max_iImg)
118  iImg = max_iImg;
119  if (jImg > max_jImg)
120  jImg = max_jImg;
121 
122  gradientX += SobelX[i][j] * I((unsigned int)iImg, (unsigned int)jImg);
123  }
124  }
125 
126  for (unsigned int i = 0; i < SobelY.getRows(); i++) {
127  double iImg = iSite + (i - offset);
128  for (unsigned int j = 0; j < SobelY.getCols(); j++) {
129  double jImg = jSite + (j - offset);
130 
131  if (iImg < 0)
132  iImg = 0.0;
133  if (jImg < 0)
134  jImg = 0.0;
135 
136  if (iImg > max_iImg)
137  iImg = max_iImg;
138  if (jImg > max_jImg)
139  jImg = max_jImg;
140 
141  gradientY += SobelY[i][j] * I((unsigned int)iImg, (unsigned int)jImg);
142  }
143  }
144 
145  double angle = atan2(gradientY, gradientX);
146  while (angle < 0)
147  angle += M_PI;
148  while (angle > M_PI)
149  angle -= M_PI;
150 
151  vecGrad[0] = cos(angle);
152  vecGrad[1] = sin(angle);
153  vecGrad.normalize();
154 
155  double angle1 = acos(vecSite * vecGrad);
156  double angle2 = acos(vecSite * (-vecGrad));
157 
158  if (display) {
159  vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(theta)),
160  static_cast<int>(it->get_j() + length * sin(theta)), vpColor::blue,
161  length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
162  if (angle1 < angle2) {
163  vpDisplay::displayArrow(I, it->get_i(), it->get_j(), static_cast<int>(it->get_i() + length * cos(angle)),
164  static_cast<int>(it->get_j() + length * sin(angle)), vpColor::red,
165  length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
166  }
167  else {
168  vpDisplay::displayArrow(I, it->get_i(), it->get_j(),
169  static_cast<int>(it->get_i() + length * cos(angle + M_PI)),
170  static_cast<int>(it->get_j() + length * sin(angle + M_PI)), vpColor::red,
171  length >= 20 ? length / 5 : 4, length >= 20 ? length / 10 : 2, thickness);
172  }
173  }
174 
175  sumErrorRad += std::min<double>(angle1, angle2);
176 
177  nbFeatures++;
178  }
179  }
180 }
181 
182 void vpMbtMeEllipse::initTracking(const vpImage<unsigned char> &I, const vpImagePoint &center_p, double n20_p,
183  double n11_p, double n02_p, bool doNotTrack, vpImagePoint *pt1,
184  const vpImagePoint *pt2)
185 {
186  if (pt1 != nullptr && pt2 != nullptr) {
187  m_trackArc = true;
188  }
189 
190  // useful for sample(I) : uc, vc, a, b, e, Ki, alpha1, alpha2
191  m_uc = center_p.get_u();
192  m_vc = center_p.get_v();
193  m_n20 = n20_p;
194  m_n11 = n11_p;
195  m_n02 = n02_p;
196 
197  computeAbeFromNij();
198  computeKiFromNij();
199 
200  if (m_trackArc) {
201  m_alpha1 = computeAngleOnEllipse(*pt1);
202  m_alpha2 = computeAngleOnEllipse(*pt2);
203  if ((m_alpha2 <= m_alpha1) || (std::fabs(m_alpha2 - m_alpha1) < m_arcEpsilon)) {
204  m_alpha2 += 2.0 * M_PI;
205  }
206  // useful for track(I)
207  m_iP1 = *pt1;
208  m_iP2 = *pt2;
209  }
210  else {
211  m_alpha1 = 0.0;
212  m_alpha2 = 2.0 * M_PI;
213  // useful for track(I)
214  vpImagePoint ip;
215  computePointOnEllipse(m_alpha1, ip);
216  m_iP1 = ip;
217  m_iP2 = ip;
218  }
219  // useful for display(I) so useless if no display before track(I)
220  m_iPc.set_uv(m_uc, m_vc);
221 
222  sample(I, doNotTrack);
223 
224  try {
225  if (!doNotTrack)
226  track(I);
227  }
228  catch (const vpException &exception) {
229  throw(exception);
230  }
231 }
232 
238 void vpMbtMeEllipse::track(const vpImage<unsigned char> &I)
239 {
240  try {
242  if (m_mask != nullptr) {
243  // Expected density could be modified if some vpMeSite are no more tracked because they are outside the mask.
244  m_expectedDensity = static_cast<unsigned int>(m_meList.size());
245  }
246  }
247  catch (const vpException &exception) {
248  throw(exception);
249  }
250 }
251 
255 void vpMbtMeEllipse::updateParameters(const vpImage<unsigned char> &I, const vpImagePoint &center_p, double n20_p,
256  double n11_p, double n02_p)
257 {
258  m_uc = center_p.get_u();
259  m_vc = center_p.get_v();
260  m_n20 = n20_p;
261  m_n11 = n11_p;
262  m_n02 = n02_p;
263 
264  computeAbeFromNij();
265  computeKiFromNij();
266 
267  suppressPoints();
268  reSample(I);
269 
270  // remet a jour l'angle delta pour chaque point de la liste
271  updateTheta();
272 }
273 
287 void vpMbtMeEllipse::reSample(const vpImage<unsigned char> &I)
288 {
289  if (!m_me) {
290  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
291  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
292  }
293 
294  unsigned int n = numberOfSignal();
295  if ((double)n < 0.9 * m_expectedDensity) {
296  sample(I);
298  }
299 }
300 
313 void vpMbtMeEllipse::sample(const vpImage<unsigned char> &I, bool doNotTrack)
314 {
315  // Warning: similar code in vpMeEllipse::sample() except for display that is removed here
316  if (!m_me) {
317  throw(vpException(vpException::fatalError, "Moving edges on ellipse not initialized"));
318  }
319  // Delete old lists
320  m_meList.clear();
321  m_angleList.clear();
322 
323  int nbrows = static_cast<int>(I.getHeight());
324  int nbcols = static_cast<int>(I.getWidth());
325 
326  if (std::fabs(m_me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
327  std::cout << "In vpMeEllipse::sample: ";
328  std::cout << "function called with sample step = 0, set to 10 dg";
329  m_me->setSampleStep(10.0);
330  }
331  double incr = vpMath::rad(m_me->getSampleStep()); // angle increment
332  // alpha2 - alpha1 = 2 * M_PI for a complete ellipse
333  m_expectedDensity = static_cast<unsigned int>(floor((m_alpha2 - m_alpha1) / incr));
334 
335  // starting angle for sampling
336  double ang = m_alpha1 + ((m_alpha2 - m_alpha1) - static_cast<double>(m_expectedDensity) * incr) / 2.0;
337  // sample positions
338  for (unsigned int i = 0; i < m_expectedDensity; i++) {
339  vpImagePoint iP;
340  computePointOnEllipse(ang, iP);
341  // If point is in the image, add to the sample list
342  // Check done in (i,j) frame)
343  if (!outOfImage(vpMath::round(iP.get_i()), vpMath::round(iP.get_j()), 0, nbrows, nbcols)) {
344  double theta = computeTheta(iP);
345  vpMeSite pix;
346  // (i,j) frame used for vpMeSite
347  pix.init(iP.get_i(), iP.get_j(), theta);
348  pix.setDisplay(m_selectDisplay);
350  m_meList.push_back(pix);
351  m_angleList.push_back(ang);
352  }
353  ang += incr;
354  }
355  if (!doNotTrack) {
357  }
358 }
359 
364 void vpMbtMeEllipse::suppressPoints()
365 {
366  // Loop through list of sites to track
367  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end();) {
368  vpMeSite s = *it; // current reference pixel
370  it = m_meList.erase(it);
371  else
372  ++it;
373  }
374 }
375 
376 #endif // #ifndef DOXYGEN_SHOULD_SKIP_THIS
unsigned int getCols() const
Definition: vpArray2D.h:327
unsigned int getRows() const
Definition: vpArray2D.h:337
Implementation of column vector and the associated operations.
Definition: vpColVector.h:163
static const vpColor red
Definition: vpColor.h:211
static const vpColor blue
Definition: vpColor.h:217
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:59
@ fatalError
Fatal error.
Definition: vpException.h:84
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
double get_j() const
Definition: vpImagePoint.h:125
double get_u() const
Definition: vpImagePoint.h:136
void set_uv(double u, double v)
Definition: vpImagePoint.h:352
double get_i() const
Definition: vpImagePoint.h:114
double get_v() const
Definition: vpImagePoint.h:147
unsigned int getWidth() const
Definition: vpImage.h:245
unsigned int getHeight() const
Definition: vpImage.h:184
static double rad(double deg)
Definition: vpMath.h:127
static int round(double x)
Definition: vpMath.h:403
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:146
Class that tracks an ellipse using moving edges.
Definition: vpMeEllipse.h:94
Performs search in a given direction(normal) for a given distance(pixels) for a given 'site'....
Definition: vpMeSite.h:65
@ NO_SUPPRESSION
Point successfully tracked.
Definition: vpMeSite.h:83
void setDisplay(vpMeSiteDisplayType select)
Definition: vpMeSite.h:247
void init()
Definition: vpMeSite.cpp:55
vpMeSiteState getState() const
Definition: vpMeSite.h:266
void setState(const vpMeSiteState &flag)
Definition: vpMeSite.h:256
void initTracking(const vpImage< unsigned char > &I)
void track(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:450