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