Visual Servoing Platform  version 3.6.1 under development (2025-01-25)
vpMeNurbs.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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  * Description:
31  * Moving edges.
32  */
33 
39 #include <cmath> // std::fabs
40 #include <limits> // numeric_limits
41 #include <stdlib.h>
42 #include <visp3/core/vpImageConvert.h>
43 #include <visp3/core/vpImageFilter.h>
44 #include <visp3/core/vpImagePoint.h>
45 #include <visp3/core/vpImageTools.h>
46 #include <visp3/core/vpMath.h>
47 #include <visp3/core/vpRect.h>
48 #include <visp3/core/vpRobust.h>
49 #include <visp3/core/vpTrackingException.h>
50 #include <visp3/me/vpMe.h>
51 #include <visp3/me/vpMeNurbs.h>
52 #include <visp3/me/vpMeSite.h>
53 #include <visp3/me/vpMeTracker.h>
54 #if defined(HAVE_OPENCV_IMGPROC)
55 #include <opencv2/imgproc/imgproc.hpp>
56 #if (VISP_HAVE_OPENCV_VERSION < 0x050000)
57 #include <opencv2/imgproc/imgproc_c.h>
58 #endif
59 #endif
60 
61 BEGIN_VISP_NAMESPACE
62 double computeDelta(double deltai, double deltaj);
63 void findAngle(const vpImage<unsigned char> &I, const vpImagePoint &iP, vpMe *me, double &angle, double &convlt);
64 vpImagePoint findFirstBorder(const vpImage<unsigned char> &Isub, const vpImagePoint &iP);
65 bool findCenterPoint(std::list<vpImagePoint> *ip_edges_list);
66 
67 // Compute the angle delta = arctan(deltai/deltaj)
68 // and normalize it between 0 and pi
69 double computeDelta(double deltai, double deltaj)
70 {
71  double delta;
72  delta = atan2(deltai, deltaj);
73  delta -= M_PI / 2.0;
74  while (delta > M_PI) {
75  delta -= M_PI;
76  }
77  while (delta < 0) {
78  delta += M_PI;
79  }
80  return (delta);
81 }
82 
83 // Check if the image point is in the image and not to close to
84 // its edge to enable the computation of a convolution with a mask.
85 static bool outOfImage(const vpImagePoint &iP, int half, int rows, int cols)
86 {
87  return ((iP.get_i() < half + 1) || (iP.get_i() > (rows - half - 3)) || (iP.get_j() < half + 1) ||
88  (iP.get_j() > (cols - half - 3)));
89 }
90 
91 // if iP is a edge point, it computes the angle corresponding to the
92 // highest convolution result. the angle is between 0 an 179.
93 // The result gives the angle in RADIAN + pi/2 (to deal with the moving edeg
94 // alpha angle) and the corresponding convolution result.
95 void findAngle(const vpImage<unsigned char> &I, const vpImagePoint &iP, vpMe *me, double &angle, double &convlt)
96 {
97  int Iheight = (int)I.getHeight();
98  int Iwidth = (int)I.getWidth();
99  angle = 0.0;
100  convlt = 0.0;
101  for (int i = 0; i < 180; i++) {
102  double conv = 0.0;
103  unsigned int half;
104  half = (me->getMaskSize() - 1) >> 1;
105 
106  if (outOfImage(iP, (int)half + me->getStrip(), Iheight, Iwidth)) {
107  conv = 0.0;
108  }
109  else {
110  int index_mask;
111 
112  if (me->getAngleStep() != 0)
113  index_mask = (int)(i / (double)me->getAngleStep());
114  else
115  throw(vpException(vpException::divideByZeroError, "angle step = 0"));
116 
117  unsigned int ihalf = (unsigned int)(iP.get_i() - half);
118  unsigned int jhalf = (unsigned int)(iP.get_j() - half);
119  unsigned int a;
120  unsigned int b;
121  for (a = 0; a < me->getMaskSize(); a++) {
122  unsigned int ihalfa = ihalf + a;
123  for (b = 0; b < me->getMaskSize(); b++) {
124  conv += me->getMask()[index_mask][a][b] * I(ihalfa, jhalf + b);
125  }
126  }
127  }
128  conv = fabs(conv);
129  if (conv > convlt) {
130  convlt = conv;
131  angle = vpMath::rad(i);
132  angle += M_PI / 2;
133  while (angle > M_PI) {
134  angle -= M_PI;
135  }
136  while (angle < 0) {
137  angle += M_PI;
138  }
139  }
140  }
141 }
142 
143 // Find the point belonging to the edge of the sub image which respects the
144 // following hypotheses:
145 //- the value of the pixel is upper than zero.
146 //- the distantce between the point and iP is less than 4 pixels.
147 // The function returns the nearest point of iP which respect the hypotheses
148 // If no point is found the returned point is (-1,-1)
149 vpImagePoint findFirstBorder(const vpImage<unsigned char> &Isub, const vpImagePoint &iP)
150 {
151  double dist = 1e6;
152  double dist_1 = 1e6;
153  vpImagePoint index(-1, -1);
154  for (unsigned int i = 0; i <= Isub.getHeight(); i++) {
155  for (unsigned int j = 0; j <= Isub.getWidth(); j++) {
156  if (i == 0 || i == Isub.getHeight() - 1 || j == 0 || j == Isub.getWidth() - 1) {
157  if (Isub(i, j) > 0) {
159  if (dist <= 16 && dist < dist_1) {
160  dist_1 = dist;
161  index.set_ij(i, j);
162  }
163  }
164  }
165  }
166  }
167  return index;
168 }
169 
170 // Check if the list of vpImagePoint contains a distant point of less tha 4
171 // pixels from the center of the sub image (ie the point (15,15).
172 bool findCenterPoint(std::list<vpImagePoint> *ip_edges_list)
173 {
174  for (std::list<vpImagePoint>::const_iterator it = ip_edges_list->begin(); it != ip_edges_list->end(); ++it) {
175  vpImagePoint iP = *it;
176  double dist = vpImagePoint::sqrDistance(iP, vpImagePoint(15, 15));
177  if (dist <= 16) {
178  return true;
179  }
180  }
181  return false;
182 }
183 
184 /***************************************/
185 
187  : nurbs(), dist(0.), nbControlPoints(20), beginPtFound(0), endPtFound(0), enableCannyDetection(false), cannyTh1(100.),
188  cannyTh2(200.)
189 { }
190 
192  : vpMeTracker(menurbs), nurbs(menurbs.nurbs), dist(0.), nbControlPoints(20), beginPtFound(0), endPtFound(0),
193  enableCannyDetection(false), cannyTh1(100.f), cannyTh2(200.f)
194 {
195  dist = menurbs.dist;
196  nbControlPoints = menurbs.nbControlPoints;
197  beginPtFound = menurbs.beginPtFound;
198  endPtFound = menurbs.endPtFound;
199  enableCannyDetection = menurbs.enableCannyDetection;
200  cannyTh1 = menurbs.cannyTh1;
201  cannyTh2 = menurbs.cannyTh2;
202 }
203 
205 {
206  std::list<vpImagePoint> ptList;
207  vpImagePoint pt;
209 
210  while (vpDisplay::getClick(I, pt, b)) {
211  if (b == vpMouseButton::button1) {
212  // std::cout<<pt<<std::endl;
213  ptList.push_back(pt);
215  vpDisplay::flush(I);
216  }
217  if (b == vpMouseButton::button3)
218  break;
219  }
220  if (ptList.size() > 3)
221  initTracking(I, ptList);
222  else
223  throw(vpException(vpException::notInitialized, "Not enough points to initialize the Nurbs"));
224 }
225 
226 void vpMeNurbs::initTracking(const vpImage<unsigned char> &I, const std::list<vpImagePoint> &ptList)
227 {
228  nurbs.globalCurveInterp(ptList);
229 
230  sample(I);
231 
233  track(I);
234 }
235 
236 void vpMeNurbs::sample(const vpImage<unsigned char> &I, bool doNotTrack)
237 {
238  (void)doNotTrack;
239  int rows = (int)I.getHeight();
240  int cols = (int)I.getWidth();
241  double step = 1.0 / (double)m_me->getPointsToTrack();
242 
243  // Delete old list
244  m_meList.clear();
245 
246  double u = 0.0;
247  vpImagePoint *pt = nullptr;
248  vpImagePoint pt_1(-rows, -cols);
249  while (u <= 1.0) {
250  if (pt != nullptr)
251  delete[] pt;
252  pt = nurbs.computeCurveDersPoint(u, 1);
253  double delta = computeDelta(pt[1].get_i(), pt[1].get_j());
254 
255  // If point is in the image, add to the sample list
256  if (!outOfImage(pt[0], 0, rows, cols) &&
258  vpMeSite pix;
259  pix.init(pt[0].get_i(), pt[0].get_j(), delta);
261 
262  m_meList.push_back(pix);
263  pt_1 = pt[0];
264  }
265  u = u + step;
266  }
267  if (pt != nullptr)
268  delete[] pt;
269 }
270 
272 {
273  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end();) {
274  vpMeSite s = *it; // current reference pixel
275 
276  if (s.getState() != vpMeSite::NO_SUPPRESSION) {
277  it = m_meList.erase(it);
278  }
279  else
280  ++it;
281  }
282 }
283 
285 {
286  double u = 0.0;
287  double d = 1e6;
288  double d_1 = 1e6;
289  std::list<vpMeSite>::iterator it = m_meList.begin();
290 
291  vpImagePoint Cu;
292  vpImagePoint *der = nullptr;
293  double step = 0.01;
294  while (u < 1 && it != m_meList.end()) {
295  vpMeSite s = *it;
296  vpImagePoint pt(s.get_i(), s.get_j());
297  while (d <= d_1 && u < 1) {
298  Cu = nurbs.computeCurvePoint(u);
299  d_1 = d;
300  d = vpImagePoint::distance(pt, Cu);
301  u += step;
302  }
303 
304  u -= step;
305  if (der != nullptr)
306  delete[] der;
307  der = nurbs.computeCurveDersPoint(u, 1);
308  // vpImagePoint toto(der[0].get_i(),der[0].get_j());
309  // vpDisplay::displayCross(I,toto,4,vpColor::red);
310 
311  s.setAlpha(computeDelta(der[1].get_i(), der[1].get_j()));
312  *it = s;
313  ++it;
314  d = 1e6;
315  d_1 = 1.5e6;
316  }
317  if (der != nullptr)
318  delete[] der;
319 }
320 
322 {
323  int rows = (int)I.getHeight();
324  int cols = (int)I.getWidth();
325 
326  vpImagePoint *begin = nullptr;
327  vpImagePoint *end = nullptr;
328 
329  begin = nurbs.computeCurveDersPoint(0.0, 1);
330  end = nurbs.computeCurveDersPoint(1.0, 1);
331 
332  // Check if the two extremities are not to close to eachother.
333  double d = vpImagePoint::distance(begin[0], end[0]);
334  double threshold = 3 * m_me->getSampleStep();
335  double sample_step = m_me->getSampleStep();
336  vpImagePoint pt;
337  if (d > threshold /*|| (m_meList.firstValue()).m_mask_sign != (m_meList.lastValue()).m_mask_sign*/) {
338  vpMeSite P;
339 
340  // Init vpMeSite
341  P.init(begin[0].get_i(), begin[0].get_j(), (m_meList.front()).getAlpha(), 0, (m_meList.front()).m_mask_sign);
343 
344  // Set the range
345  unsigned int memory_range = m_me->getRange();
346  m_me->setRange(2);
347 
348  // Point at the beginning of the list
349  bool beginPtAdded = false;
350  vpImagePoint pt_max = begin[0];
351  double angle = atan2(begin[1].get_i(), begin[1].get_j());
352  double co = vpMath::abs(cos(angle));
353  co = co * vpMath::sign(begin[1].get_j());
354  double si = vpMath::abs(sin(angle));
355  si = si * vpMath::sign(begin[1].get_i());
356  for (int i = 0; i < 3; i++) {
357  P.m_ifloat = P.m_ifloat - si * sample_step;
358  P.m_i = (int)P.m_ifloat;
359  P.m_jfloat = P.m_jfloat - co * sample_step;
360  P.m_j = (int)P.m_jfloat;
361  pt.set_ij(P.m_ifloat, P.m_jfloat);
362  if (vpImagePoint::distance(end[0], pt) < threshold)
363  break;
364  if (!outOfImage(P.get_i(), P.get_j(), 5, rows, cols)) {
365  P.track(I, m_me, false);
366 
367  if (P.getState() == vpMeSite::NO_SUPPRESSION) {
368  m_meList.push_front(P);
369  beginPtAdded = true;
370  pt_max = pt;
371  if (vpDEBUG_ENABLE(3)) {
373  }
374  }
375  else {
376  if (vpDEBUG_ENABLE(3)) {
378  }
379  }
380  }
381  }
382 
383  if (!beginPtAdded)
384  beginPtFound++;
385 
386  P.init(end[0].get_i(), end[0].get_j(), (m_meList.back()).getAlpha(), 0, (m_meList.back()).m_mask_sign);
388 
389  bool endPtAdded = false;
390  angle = atan2(end[1].get_i(), end[1].get_j());
391  co = vpMath::abs(cos(angle));
392  co = co * vpMath::sign(end[1].get_j());
393  si = vpMath::abs(sin(angle));
394  si = si * vpMath::sign(end[1].get_i());
395  for (int i = 0; i < 3; i++) {
396  P.m_ifloat = P.m_ifloat + si * sample_step;
397  P.m_i = (int)P.m_ifloat;
398  P.m_jfloat = P.m_jfloat + co * sample_step;
399  P.m_j = (int)P.m_jfloat;
400  pt.set_ij(P.m_ifloat, P.m_jfloat);
401  if (vpImagePoint::distance(begin[0], pt) < threshold)
402  break;
403  if (!outOfImage(P.get_i(), P.get_j(), 5, rows, cols)) {
404  P.track(I, m_me, false);
405 
406  if (P.getState() == vpMeSite::NO_SUPPRESSION) {
407  m_meList.push_back(P);
408  endPtAdded = true;
409  if (vpDEBUG_ENABLE(3)) {
411  }
412  }
413  else {
414  if (vpDEBUG_ENABLE(3)) {
416  }
417  }
418  }
419  }
420  if (!endPtAdded)
421  endPtFound++;
422  m_me->setRange(memory_range);
423  }
424  else {
425  m_meList.pop_front();
426  }
427  /*if(begin != nullptr)*/ delete[] begin;
428  /*if(end != nullptr) */ delete[] end;
429 }
430 
432 {
433  vpMeSite pt = m_meList.front();
434  vpImagePoint firstPoint(pt.m_ifloat, pt.m_jfloat);
435  pt = m_meList.back();
436  vpImagePoint lastPoint(pt.m_ifloat, pt.m_jfloat);
437  if (beginPtFound >= 3 && farFromImageEdge(I, firstPoint)) {
438  vpImagePoint *begin = nullptr;
439  begin = nurbs.computeCurveDersPoint(0.0, 1);
440  vpImage<unsigned char> Isub(32, 32); // Sub image.
441  vpImagePoint topLeft(begin[0].get_i() - 15, begin[0].get_j() - 15);
442  vpRect rect(topLeft, 32, 32);
443 
445 
446  vpImageTools::crop(I, rect, Isub);
447 
448  vpImagePoint lastPtInSubIm(begin[0]);
449  double u = 0.0;
450  double step = 0.0001;
451  // Find the point of the nurbs closest from the edge of the subImage and
452  // in the subImage.
453  while (inRectangle(lastPtInSubIm, rect) && u < 1) {
454  u += step;
455  lastPtInSubIm = nurbs.computeCurvePoint(u);
456  }
457 
458  u -= step;
459  if (u > 0)
460  lastPtInSubIm = nurbs.computeCurvePoint(u);
461 
462  vpImageFilter::canny(Isub, Isub, 3, cannyTh1, 3);
463 
464  vpImagePoint firstBorder(-1, -1);
465 
466  firstBorder = findFirstBorder(Isub, lastPtInSubIm - topLeft);
467 
468  std::list<vpImagePoint> ip_edges_list;
469  if (firstBorder != vpImagePoint(-1, -1)) {
470  unsigned int dir;
471  double fi = static_cast<double>(firstBorder.get_i());
472  double fj = static_cast<double>(firstBorder.get_j());
473  double w = Isub.getWidth() - 1;
474  double h = Isub.getHeight() - 1;
475  // if (firstBorder.get_i() == 0) dir = 4;
476  if (std::fabs(fi) <= std::numeric_limits<double>::epsilon())
477  dir = 4;
478  // else if (firstBorder.get_i() == Isub.getHeight()-1) dir = 0;
479  else if (std::fabs(fi - h) <= std::fabs(vpMath::maximum(fi, h)) * std::numeric_limits<double>::epsilon())
480  dir = 0;
481  // else if (firstBorder.get_j() == 0) dir = 2;
482  else if (std::fabs(fj) <= std::numeric_limits<double>::epsilon())
483  dir = 2;
484  // else if (firstBorder.get_j() == Isub.getWidth()-1) dir = 6;
485  else if (std::fabs(fj - w) <= std::fabs(vpMath::maximum(fj, w)) * std::numeric_limits<double>::epsilon())
486  dir = 6;
487  computeFreemanChainElement(Isub, firstBorder, dir);
488  unsigned int firstDir = dir;
489  ip_edges_list.push_back(firstBorder);
490  vpImagePoint border(firstBorder);
491  vpImagePoint dBorder;
492  do {
493  computeFreemanParameters(dir, dBorder);
494  border = border + dBorder;
495  vpDisplay::displayPoint(I, border + topLeft, vpColor::orange);
496 
497  ip_edges_list.push_back(border);
498 
499  computeFreemanChainElement(Isub, border, dir);
500  } while ((border != firstBorder || dir != firstDir) && isInImage(Isub, border));
501  }
502 
503  if (findCenterPoint(&ip_edges_list)) {
504  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end();
505  /*++it*/) {
506  vpMeSite s = *it;
507  vpImagePoint iP(s.m_ifloat, s.m_jfloat);
508  if (inRectangle(iP, rect))
509  it = m_meList.erase(it);
510  else
511  break;
512  }
513 
514  std::list<vpMeSite>::iterator itList = m_meList.begin();
515  double convlt;
516  double delta = 0;
517  unsigned int nbr = 0;
518  std::list<vpMeSite> addedPt;
519  for (std::list<vpImagePoint>::const_iterator itEdges = ip_edges_list.begin(); itEdges != ip_edges_list.end();
520  ++itEdges) {
521  vpMeSite s = *itList;
522  vpImagePoint iPtemp = *itEdges + topLeft;
523  vpMeSite pix;
524  pix.init(iPtemp.get_i(), iPtemp.get_j(), delta);
525  dist = vpMeSite::sqrDistance(s, pix);
526  if (dist >= vpMath::sqr(m_me->getSampleStep()) /*25*/) {
527  bool exist = false;
528  for (std::list<vpMeSite>::const_iterator itAdd = addedPt.begin(); itAdd != addedPt.end(); ++itAdd) {
529  dist = vpMeSite::sqrDistance(pix, *itAdd);
530  if (dist < vpMath::sqr(m_me->getSampleStep()) /*25*/)
531  exist = true;
532  }
533  if (!exist) {
534  findAngle(I, iPtemp, m_me, delta, convlt);
535  pix.init(iPtemp.get_i(), iPtemp.get_j(), delta, convlt);
537  --itList;
538  m_meList.insert(itList, pix);
539  ++itList;
540  addedPt.push_front(pix);
541  nbr++;
542  }
543  }
544  }
545 
546  unsigned int memory_range = m_me->getRange();
547  m_me->setRange(3);
548  std::list<vpMeSite>::iterator itList2 = m_meList.begin();
549  for (unsigned int j = 0; j < nbr; j++) {
550  vpMeSite s = *itList2;
551  s.track(I, m_me, false);
552  *itList2 = s;
553  ++itList2;
554  }
555  m_me->setRange(memory_range);
556  }
557 
558  /* if (begin != nullptr) */ delete[] begin;
559  beginPtFound = 0;
560  }
561 
562  if (endPtFound >= 3 && farFromImageEdge(I, lastPoint)) {
563  vpImagePoint *end = nullptr;
564  end = nurbs.computeCurveDersPoint(1.0, 1);
565 
566  vpImage<unsigned char> Isub(32, 32); // Sub image.
567  vpImagePoint topLeft(end[0].get_i() - 15, end[0].get_j() - 15);
568  vpRect rect(topLeft, 32, 32);
569 
571 
572  vpImageTools::crop(I, rect, Isub);
573 
574  vpImagePoint lastPtInSubIm(end[0]);
575  double u = 1.0;
576  double step = 0.0001;
577  // Find the point of the nurbs closest from the edge of the subImage and
578  // in the subImage.
579  while (inRectangle(lastPtInSubIm, rect) && u > 0) {
580  u -= step;
581  lastPtInSubIm = nurbs.computeCurvePoint(u);
582  }
583 
584  u += step;
585  if (u < 1.0)
586  lastPtInSubIm = nurbs.computeCurvePoint(u);
587 
588  vpImageFilter::canny(Isub, Isub, 3, cannyTh1, 3);
589 
590  vpImagePoint firstBorder(-1, -1);
591 
592  firstBorder = findFirstBorder(Isub, lastPtInSubIm - topLeft);
593 
594  std::list<vpImagePoint> ip_edges_list;
595  if (firstBorder != vpImagePoint(-1, -1)) {
596  unsigned int dir;
597  double fi = firstBorder.get_i();
598  double fj = firstBorder.get_j();
599  double w = Isub.getWidth() - 1;
600  double h = Isub.getHeight() - 1;
601  // if (firstBorder.get_i() == 0) dir = 4;
602  if (std::fabs(fi) <= std::numeric_limits<double>::epsilon())
603  dir = 4;
604  // else if (firstBorder.get_i() == Isub.getHeight()-1) dir = 0;
605  else if (std::fabs(fi - h) <= std::fabs(vpMath::maximum(fi, h)) * std::numeric_limits<double>::epsilon())
606  dir = 0;
607  // else if (firstBorder.get_j() == 0) dir = 2;
608  else if (std::fabs(fj) <= std::numeric_limits<double>::epsilon())
609  dir = 2;
610  // else if (firstBorder.get_j() == Isub.getWidth()-1) dir = 6;
611  else if (std::fabs(fj - w) <= std::fabs(vpMath::maximum(fj, w)) * std::numeric_limits<double>::epsilon())
612  dir = 6;
613 
614  computeFreemanChainElement(Isub, firstBorder, dir);
615  unsigned int firstDir = dir;
616  ip_edges_list.push_back(firstBorder);
617  vpImagePoint border(firstBorder);
618  vpImagePoint dBorder;
619  do {
620  computeFreemanParameters(dir, dBorder);
621  border = border + dBorder;
622  vpDisplay::displayPoint(I, border + topLeft, vpColor::orange);
623 
624  ip_edges_list.push_back(border);
625 
626  computeFreemanChainElement(Isub, border, dir);
627  } while ((border != firstBorder || dir != firstDir) && isInImage(Isub, border));
628  }
629 
630  if (findCenterPoint(&ip_edges_list)) {
631  vpMeSite s;
632 
633  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it!=m_meList.end(); ++it) {
634  s = *it;
635  vpImagePoint iP(s.m_ifloat, s.m_jfloat);
636  if (inRectangle(iP, rect)) {
637  m_meList.erase(it);
638  }
639  else
640  break;
641  }
642 
643  std::list<vpMeSite>::iterator itList = m_meList.end();
644  --itList; // Move on the last element
645  double convlt;
646  double delta;
647  unsigned int nbr = 0;
648  std::list<vpMeSite> addedPt;
649  for (std::list<vpImagePoint>::const_iterator itEdges = ip_edges_list.begin(); itEdges != ip_edges_list.end();
650  ++itEdges) {
651  s = *itList;
652  vpImagePoint iPtemp = *itEdges + topLeft;
653  vpMeSite pix;
654  pix.init(iPtemp.get_i(), iPtemp.get_j(), 0);
655  dist = vpMeSite::sqrDistance(s, pix);
656  if (dist >= vpMath::sqr(m_me->getSampleStep())) {
657  bool exist = false;
658  for (std::list<vpMeSite>::const_iterator itAdd = addedPt.begin(); itAdd != addedPt.end(); ++itAdd) {
659  dist = vpMeSite::sqrDistance(pix, *itAdd);
660  if (dist < vpMath::sqr(m_me->getSampleStep()))
661  exist = true;
662  }
663  if (!exist) {
664  findAngle(I, iPtemp, m_me, delta, convlt);
665  pix.init(iPtemp.get_i(), iPtemp.get_j(), delta, convlt);
667  m_meList.push_back(pix);
668  addedPt.push_back(pix);
669  nbr++;
670  }
671  }
672  }
673 
674  unsigned int memory_range = m_me->getRange();
675  m_me->setRange(3);
676  std::list<vpMeSite>::iterator itList2 = m_meList.end();
677  --itList2; // Move to the last element
678  for (unsigned int j = 0; j < nbr; j++) {
679  vpMeSite me_s = *itList2;
680  me_s.track(I, m_me, false);
681  *itList2 = me_s;
682  --itList2;
683  }
684  m_me->setRange(memory_range);
685  }
686 
687  /* if (end != nullptr) */ delete[] end;
688  endPtFound = 0;
689  }
690 }
691 
693 {
694  unsigned int n = numberOfSignal();
695  double nbPt = floor(dist / m_me->getSampleStep());
696 
697  if ((double)n < 0.7 * nbPt) {
698  sample(I);
700  }
701 }
702 
704 {
705  int rows = (int)I.getHeight();
706  int cols = (int)I.getWidth();
707  vpImagePoint *iP = nullptr;
708 
709  int n = (int)numberOfSignal();
710 
711  std::list<vpMeSite>::iterator it = m_meList.begin();
712  std::list<vpMeSite>::iterator itNext = m_meList.begin();
713  ++itNext;
714 
715  unsigned int range_tmp = m_me->getRange();
716  m_me->setRange(2);
717 
718  while (itNext != m_meList.end() && n <= m_me->getPointsToTrack()) {
719  vpMeSite s = *it; // current reference pixel
720  vpMeSite s_next = *itNext; // current reference pixel
721 
722  double d = vpMeSite::sqrDistance(s, s_next);
723  if (d > 4 * vpMath::sqr(m_me->getSampleStep()) && d < 1600) {
724  vpImagePoint iP0(s.m_ifloat, s.m_jfloat);
725  vpImagePoint iPend(s_next.m_ifloat, s_next.m_jfloat);
726  vpImagePoint iP_1(s.m_ifloat, s.m_jfloat);
727 
728  double u = 0.0;
729  double ubegin = 0.0;
730  double uend = 0.0;
731  double dmin1_1 = 1e6;
732  double dmin2_1 = 1e6;
733  while (u < 1) {
734  u += 0.01;
735  double dmin1 = vpImagePoint::sqrDistance(nurbs.computeCurvePoint(u), iP0);
736  double dmin2 = vpImagePoint::sqrDistance(nurbs.computeCurvePoint(u), iPend);
737 
738  if (dmin1 < dmin1_1) {
739  dmin1_1 = dmin1;
740  ubegin = u;
741  }
742 
743  if (dmin2 < dmin2_1) {
744  dmin2_1 = dmin2;
745  uend = u;
746  }
747  }
748  u = ubegin;
749 
750  // if(( u != 1.0 || uend != 1.0)
751  if ((std::fabs(u - 1.0) > std::fabs(vpMath::maximum(u, 1.0)) * std::numeric_limits<double>::epsilon()) ||
752  (std::fabs(uend - 1.0) > std::fabs(vpMath::maximum(uend, 1.0)) * std::numeric_limits<double>::epsilon())) {
753  iP = nurbs.computeCurveDersPoint(u, 1);
754 
755  while (vpImagePoint::sqrDistance(iP[0], iPend) > vpMath::sqr(m_me->getSampleStep()) && u < uend) {
756  u += 0.01;
757  /*if (iP!=nullptr)*/ {
758  delete[] iP;
759  iP = nullptr;
760  }
761  iP = nurbs.computeCurveDersPoint(u, 1);
762  if (vpImagePoint::sqrDistance(iP[0], iP_1) > vpMath::sqr(m_me->getSampleStep()) &&
763  !outOfImage(iP[0], 0, rows, cols)) {
764  double delta = computeDelta(iP[1].get_i(), iP[1].get_j());
765  vpMeSite pix;
766  pix.init(iP[0].get_i(), iP[0].get_j(), delta);
768  pix.track(I, m_me, false);
769  if (pix.getState() == vpMeSite::NO_SUPPRESSION) {
770  m_meList.insert(it, pix);
771  iP_1 = iP[0];
772  }
773  }
774  }
775  /*if (iP!=nullptr)*/ {
776  delete[] iP;
777  iP = nullptr;
778  }
779  }
780  }
781  ++it;
782  ++itNext;
783  }
784  m_me->setRange(range_tmp);
785 }
786 
788 {
789  std::list<vpMeSite>::const_iterator it = m_meList.begin();
790  std::list<vpMeSite>::iterator itNext = m_meList.begin();
791  ++itNext;
792  for (; itNext != m_meList.end();) {
793  vpMeSite s = *it; // current reference pixel
794  vpMeSite s_next = *itNext; // current reference pixel
795 
796  if (vpMeSite::sqrDistance(s, s_next) < vpMath::sqr(m_me->getSampleStep())) {
798 
799  *itNext = s_next;
800  ++it;
801  ++itNext;
802  if (itNext != m_meList.end()) {
803  ++it;
804  ++itNext;
805  }
806  }
807  else {
808  ++it;
809  ++itNext;
810  }
811  }
812 }
813 
815 {
816  // Tracking des vpMeSites
818 
819  // Suppress points which are too close to each other
821 
822  // Suppressions des points ejectes par le tracking
823  suppressPoints();
824 
825  if (m_meList.size() == 1)
826  throw(vpTrackingException(vpTrackingException::notEnoughPointError, "Not enough valid me to track"));
827 
828  // Recalcule les parametres
829  // nurbs.globalCurveInterp(m_meList);
830  nurbs.globalCurveApprox(m_meList, nbControlPoints);
831 
832  // On resample localement
833  localReSample(I);
834 
835  seekExtremities(I);
836  if (enableCannyDetection)
838 
839  // nurbs.globalCurveInterp(m_meList);
840  nurbs.globalCurveApprox(m_meList, nbControlPoints);
841 
842  double u = 0.0;
843  vpImagePoint pt;
844  vpImagePoint pt_1;
845  dist = 0;
846  while (u <= 1.0) {
847  pt = nurbs.computeCurvePoint(u);
848  // if(u!=0)
849  if (std::fabs(u) > std::numeric_limits<double>::epsilon())
850  dist = dist + vpImagePoint::distance(pt, pt_1);
851  pt_1 = pt;
852  u = u + 0.01;
853  }
854 
855  updateDelta();
856 
857  reSample(I);
858 }
859 
860 void vpMeNurbs::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness)
861 {
862  vpMeNurbs::display(I, nurbs, color, thickness);
863 }
864 
881 bool vpMeNurbs::computeFreemanChainElement(const vpImage<unsigned char> &I, vpImagePoint &iP, unsigned int &element)
882 {
883  vpImagePoint diP;
884  vpImagePoint iPtemp;
885  if (hasGoodLevel(I, iP)) {
886  // get the point on the right of the point passed in
887  computeFreemanParameters((element + 2) % 8, diP);
888  iPtemp = iP + diP;
889  if (hasGoodLevel(I, iPtemp)) {
890  element = (element + 2) % 8; // turn right
891  }
892  else {
893  computeFreemanParameters((element + 1) % 8, diP);
894  iPtemp = iP + diP;
895 
896  if (hasGoodLevel(I, iPtemp)) {
897  element = (element + 1) % 8; // turn diag right
898  }
899  else {
900  computeFreemanParameters(element, diP);
901  iPtemp = iP + diP;
902 
903  if (hasGoodLevel(I, iPtemp)) {
904  // element = element; // keep same dir
905  }
906  else {
907  computeFreemanParameters((element + 7) % 8, diP);
908  iPtemp = iP + diP;
909 
910  if (hasGoodLevel(I, iPtemp)) {
911  element = (element + 7) % 8; // turn diag left
912  }
913  else {
914  computeFreemanParameters((element + 6) % 8, diP);
915  iPtemp = iP + diP;
916 
917  if (hasGoodLevel(I, iPtemp)) {
918  element = (element + 6) % 8; // turn left
919  }
920  else {
921  computeFreemanParameters((element + 5) % 8, diP);
922  iPtemp = iP + diP;
923 
924  if (hasGoodLevel(I, iPtemp)) {
925  element = (element + 5) % 8; // turn diag down
926  }
927  else {
928  computeFreemanParameters((element + 4) % 8, diP);
929  iPtemp = iP + diP;
930 
931  if (hasGoodLevel(I, iPtemp)) {
932  element = (element + 4) % 8; // turn down
933  }
934  else {
935  computeFreemanParameters((element + 3) % 8, diP);
936  iPtemp = iP + diP;
937 
938  if (hasGoodLevel(I, iPtemp)) {
939  element = (element + 3) % 8; // turn diag right down
940  }
941  else {
942  // No neighbor with a good level
943  //
944  return false;
945  }
946  }
947  }
948  }
949  }
950  }
951  }
952  }
953  }
954  else {
955  return false;
956  }
957  return true;
958 }
959 
972 bool vpMeNurbs::hasGoodLevel(const vpImage<unsigned char> &I, const vpImagePoint &iP) const
973 {
974  if (!isInImage(I, iP))
975  return false;
976 
977  if (I((unsigned int)vpMath::round(iP.get_i()), (unsigned int)vpMath::round(iP.get_j())) > 0) {
978  return true;
979  }
980  else {
981  return false;
982  }
983 }
984 
995 bool vpMeNurbs::isInImage(const vpImage<unsigned char> &I, const vpImagePoint &iP) const
996 {
997  return (iP.get_i() >= 0 && iP.get_j() >= 0 && iP.get_i() < I.getHeight() && iP.get_j() < I.getWidth());
998 }
999 
1017 void vpMeNurbs::computeFreemanParameters(unsigned int element, vpImagePoint &diP)
1018 {
1019  /*
1020  5 6 7
1021  \ | /
1022  \|/
1023  4 ------- 0
1024  /|\
1025  / | \
1026  3 2 1
1027  */
1028  switch (element) {
1029  case 0: // go right
1030  diP.set_ij(0, 1);
1031  break;
1032 
1033  case 1: // go right top
1034  diP.set_ij(1, 1);
1035  break;
1036 
1037  case 2: // go top
1038  diP.set_ij(1, 0);
1039  break;
1040 
1041  case 3:
1042  diP.set_ij(1, -1);
1043  break;
1044 
1045  case 4:
1046  diP.set_ij(0, -1);
1047  break;
1048 
1049  case 5:
1050  diP.set_ij(-1, -1);
1051  break;
1052 
1053  case 6:
1054  diP.set_ij(-1, 0);
1055  break;
1056 
1057  case 7:
1058  diP.set_ij(-1, 1);
1059  break;
1060  }
1061 }
1062 
1072 bool vpMeNurbs::farFromImageEdge(const vpImage<unsigned char> &I, const vpImagePoint &iP)
1073 {
1074  unsigned int height = I.getHeight();
1075  unsigned int width = I.getWidth();
1076  return (iP.get_i() < height - 20 && iP.get_j() < width - 20 && iP.get_i() > 20 && iP.get_j() > 20);
1077 }
1078 
1079 void vpMeNurbs::display(const vpImage<unsigned char> &I, vpNurbs &n, const vpColor &color, unsigned int thickness)
1080 {
1081  double u = 0.0;
1082  vpImagePoint pt;
1083  while (u <= 1) {
1084  pt = n.computeCurvePoint(u);
1085  vpDisplay::displayCross(I, pt, 4, color, thickness);
1086  u += 0.01;
1087  }
1088 }
1089 
1090 void vpMeNurbs::display(const vpImage<vpRGBa> &I, vpNurbs &n, const vpColor &color, unsigned int thickness)
1091 {
1092  double u = 0.0;
1093  vpImagePoint pt;
1094  while (u <= 1) {
1095  pt = n.computeCurvePoint(u);
1096  vpDisplay::displayCross(I, pt, 4, color, thickness);
1097  u += 0.01;
1098  }
1099 }
1100 END_VISP_NAMESPACE
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static const vpColor orange
Definition: vpColor.h:208
static const vpColor blue
Definition: vpColor.h:204
static const vpColor green
Definition: vpColor.h:201
static bool getClick(const vpImage< unsigned char > &I, bool blocking=true)
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)
static void displayPoint(const vpImage< unsigned char > &I, const vpImagePoint &ip, const vpColor &color, unsigned int thickness=1)
static void displayRectangle(const vpImage< unsigned char > &I, const vpImagePoint &topLeft, unsigned int width, unsigned int height, const vpColor &color, bool fill=false, unsigned int thickness=1)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ notInitialized
Used to indicate that a parameter is not initialized.
Definition: vpException.h:74
@ divideByZeroError
Division by zero.
Definition: vpException.h:70
static void canny(const vpImage< unsigned char > &I, vpImage< unsigned char > &Ic, const unsigned int &gaussianFilterSize, const float &thresholdCanny, const unsigned int &apertureSobel)
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
static double distance(const vpImagePoint &iP1, const vpImagePoint &iP2)
void set_ij(double ii, double jj)
Definition: vpImagePoint.h:320
static double sqrDistance(const vpImagePoint &iP1, const vpImagePoint &iP2)
double get_i() const
Definition: vpImagePoint.h:114
static void crop(const vpImage< Type > &I, double roi_top, double roi_left, unsigned int roi_height, unsigned int roi_width, vpImage< Type > &crop, unsigned int v_scale=1, unsigned int h_scale=1)
Definition: vpImageTools.h:315
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 Type maximum(const Type &a, const Type &b)
Definition: vpMath.h:254
static double sqr(double x)
Definition: vpMath.h:203
static Type abs(const Type &x)
Definition: vpMath.h:269
static int round(double x)
Definition: vpMath.h:410
static int sign(double x)
Definition: vpMath.h:429
Class that tracks in an image a edge defined by a Nurbs.
Definition: vpMeNurbs.h:133
void track(const vpImage< unsigned char > &I)
Definition: vpMeNurbs.cpp:814
void reSample(const vpImage< unsigned char > &I)
Definition: vpMeNurbs.cpp:692
void seekExtremities(const vpImage< unsigned char > &I)
Definition: vpMeNurbs.cpp:321
virtual void sample(const vpImage< unsigned char > &I, bool doNotTrack=false)
Definition: vpMeNurbs.cpp:236
void initTracking(const vpImage< unsigned char > &I)
Definition: vpMeNurbs.cpp:204
void updateDelta()
Definition: vpMeNurbs.cpp:284
void localReSample(const vpImage< unsigned char > &I)
Definition: vpMeNurbs.cpp:703
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1)
Definition: vpMeNurbs.cpp:860
void seekExtremitiesCanny(const vpImage< unsigned char > &I)
Definition: vpMeNurbs.cpp:431
void suppressPoints()
Definition: vpMeNurbs.cpp:271
void supressNearPoints()
Definition: vpMeNurbs.cpp:787
Performs search in a given direction(normal) for a given distance(pixels) for a given 'site'....
Definition: vpMeSite.h:68
@ TOO_NEAR
Point not tracked anymore, since too near from its neighbor.
Definition: vpMeSite.h:93
@ NO_SUPPRESSION
Point successfully tracked.
Definition: vpMeSite.h:86
void setDisplay(vpMeSiteDisplayType select)
Definition: vpMeSite.h:264
double m_ifloat
Subpixel coordinates along i of a site.
Definition: vpMeSite.h:103
void setAlpha(const double &a)
Definition: vpMeSite.h:259
void init()
Definition: vpMeSite.cpp:70
vpMeSiteState getState() const
Definition: vpMeSite.h:283
int m_j
Integer coordinates along j of a site.
Definition: vpMeSite.h:101
int get_j() const
Definition: vpMeSite.h:189
int m_i
Integer coordinate along i of a site.
Definition: vpMeSite.h:99
double m_jfloat
Subpixel coordinates along j of a site.
Definition: vpMeSite.h:105
int get_i() const
Definition: vpMeSite.h:183
static double sqrDistance(const vpMeSite &S1, const vpMeSite &S2)
Definition: vpMeSite.h:384
void track(const vpImage< unsigned char > &I, const vpMe *me, const bool &test_contrast=true)
Definition: vpMeSite.cpp:282
void setState(const vpMeSiteState &flag)
Definition: vpMeSite.h:273
Contains abstract elements for a Distance to Feature type feature.
Definition: vpMeTracker.h:62
void initTracking(const vpImage< unsigned char > &I)
unsigned int numberOfSignal()
Definition: vpMeTracker.cpp:94
vpMeSite::vpMeSiteDisplayType m_selectDisplay
Moving-edges display type.
Definition: vpMeTracker.h:323
void track(const vpImage< unsigned char > &I)
vpMe * m_me
Moving edges initialisation parameters.
Definition: vpMeTracker.h:313
bool outOfImage(int i, int j, int border, int nrows, int ncols)
std::list< vpMeSite > m_meList
Definition: vpMeTracker.h:311
Definition: vpMe.h:134
void setRange(const unsigned int &range)
Definition: vpMe.h:415
unsigned int getAngleStep() const
Definition: vpMe.h:193
int getPointsToTrack() const
Definition: vpMe.h:261
int getStrip() const
Definition: vpMe.h:282
unsigned int getMaskSize() const
Definition: vpMe.h:225
vpMatrix * getMask() const
Definition: vpMe.h:200
double getSampleStep() const
Definition: vpMe.h:275
unsigned int getRange() const
Definition: vpMe.h:268
Class that provides tools to compute and manipulate a Non Uniform Rational B-Spline curve.
Definition: vpNurbs.h:93
static void globalCurveInterp(std::vector< vpImagePoint > &l_crossingPoints, unsigned int l_p, std::vector< double > &l_knots, std::vector< vpImagePoint > &l_controlPoints, std::vector< double > &l_weights)
Definition: vpNurbs.cpp:466
static vpImagePoint computeCurvePoint(double l_u, unsigned int l_i, unsigned int l_p, std::vector< double > &l_knots, std::vector< vpImagePoint > &l_controlPoints, std::vector< double > &l_weights)
Definition: vpNurbs.cpp:56
static vpImagePoint * computeCurveDersPoint(double l_u, unsigned int l_i, unsigned int l_p, unsigned int l_der, std::vector< double > &l_knots, std::vector< vpImagePoint > &l_controlPoints, std::vector< double > &l_weights)
Definition: vpNurbs.cpp:156
static void globalCurveApprox(std::vector< vpImagePoint > &l_crossingPoints, unsigned int l_p, unsigned int l_n, std::vector< double > &l_knots, std::vector< vpImagePoint > &l_controlPoints, std::vector< double > &l_weights)
Definition: vpNurbs.cpp:595
Defines a rectangle in the plane.
Definition: vpRect.h:79
Error that can be emitted by the vpTracker class and its derivatives.
@ notEnoughPointError
Not enough point to track.