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