Visual Servoing Platform  version 3.6.1 under development (2025-02-01)
vpMeLine.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 <algorithm> // (std::min)
40 #include <cmath> // std::fabs
41 #include <limits> // numeric_limits
42 #include <stdlib.h>
43 #include <visp3/core/vpDebug.h>
44 #include <visp3/core/vpImagePoint.h>
45 #include <visp3/core/vpMath.h>
46 #include <visp3/core/vpRobust.h>
47 #include <visp3/core/vpTrackingException.h>
48 #include <visp3/core/vpMatrixException.h>
49 #include <visp3/me/vpMe.h>
50 #include <visp3/me/vpMeLine.h>
51 #include <visp3/me/vpMeSite.h>
52 #include <visp3/me/vpMeTracker.h>
53 
54 #define INCR_MIN 1
55 
56 BEGIN_VISP_NAMESPACE
57 
58 void vpMeLine::normalizeAngle(double &delta)
59 {
60  // angle in [- M_PI; M_PI]
61  while (delta > M_PI) {
62  delta -= M_PI;
63  }
64  while (delta < -M_PI) {
65  delta += M_PI;
66  }
67 }
68 
69 void vpMeLine::computeDelta(double &delta, double i1, double j1, double i2, double j2)
70 {
71 
72  double B = (double)(i1 - i2);
73  double A = (double)(j1 - j2);
74 
75  delta = atan2(B, A);
76  delta -= M_PI / 2.0;
77  normalizeAngle(delta);
78 }
79 
86 void vpMeLine::project(double a, double b, double c, const vpMeSite &P, vpImagePoint &iP)
87 {
88  const double i = P.m_ifloat;
89  const double j = P.m_jfloat;
90  double ip, jp;
91 
92  double norm = a*a + b*b; // cannot be 0
93  double cross = b*i - a*j;
94  ip = (b*cross - a*c)/norm;
95  jp = (-a*cross - b*c)/norm;
96 
97  iP.set_i(ip);
98  iP.set_j(jp);
99 }
100 
102  : m_rho(0.), m_theta(0.), m_delta(0.), m_delta_1(0.), m_angle(0.), m_angle_1(90), m_sign(1),
103  m_useIntensityForRho(true), m_a(0.), m_b(0.), m_c(0.)
104 { }
105 
107  : vpMeTracker(meline), m_rho(0.), m_theta(0.), m_delta(0.), m_delta_1(0.), m_angle(0.), m_angle_1(90), m_sign(1),
108  m_useIntensityForRho(true), m_a(0.), m_b(0.), m_c(0.)
109 {
110  m_rho = meline.m_rho;
111  m_theta = meline.m_theta;
112  m_delta = meline.m_delta;
113  m_delta_1 = meline.m_delta_1;
114  m_angle = meline.m_angle;
115  m_angle_1 = meline.m_angle_1;
116  m_sign = meline.m_sign;
117 
118  m_a = meline.m_a;
119  m_b = meline.m_b;
120  m_c = meline.m_c;
122  m_PExt[0] = meline.m_PExt[0];
123  m_PExt[1] = meline.m_PExt[1];
124 }
125 
127 {
128  m_meList.clear();
129 }
130 
131 void vpMeLine::sample(const vpImage<unsigned char> &I, bool doNotTrack)
132 {
133  (void)doNotTrack;
134  if (!m_me) {
135  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
136  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
137  }
138 
139  double sampleStep = fabs(m_me->getSampleStep());
140  if (sampleStep <= std::numeric_limits<double>::epsilon()) {
141  vpERROR_TRACE("function called with sample step = 0");
142  throw(vpTrackingException(vpTrackingException::fatalError, "sample step = 0"));
143  }
144 
145  // i, j portions of the line_p
146  double diffsi = m_PExt[1].m_ifloat - m_PExt[0].m_ifloat;
147  double diffsj = m_PExt[1].m_jfloat - m_PExt[0].m_jfloat;
148 
149  double length_p = sqrt((vpMath::sqr(diffsi) + vpMath::sqr(diffsj)));
150  if (length_p < (2.0 * sampleStep)) {
151  throw(vpTrackingException(vpTrackingException::fatalError, "Points too close together to define a line"));
152  }
153  // number of samples along line_p
154  double n_sample = length_p / sampleStep;
155 
156  double stepi = diffsi / n_sample;
157  double stepj = diffsj / n_sample;
158 
159  // Choose starting point
160  double is = m_PExt[0].m_ifloat;
161  double js = m_PExt[0].m_jfloat;
162 
163  // Delete old list
164  m_meList.clear();
165 
166  // sample positions at i*m_me->getSampleStep() interval along the line_p, starting at PSiteExt[0]
167  int nbrows = static_cast<int>(I.getHeight());
168  int nbcols = static_cast<int>(I.getWidth());
169  const double marginRatio = m_me->getThresholdMarginRatio();
170 
171  for (int i = 0; i <= vpMath::round(n_sample); ++i) {
172  vpImagePoint iP;
173  iP.set_ij(is, js);
174  if (!outOfImage(iP, 5, nbrows, nbcols)) {
175  unsigned int is_uint = static_cast<unsigned int>(is);
176  unsigned int js_uint = static_cast<unsigned int>(js);
177  if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
178  vpMeSite pix;
179  pix.init(is, js, m_delta, 0, m_sign);
182  double convolution = pix.convolution(I, m_me);
183  double contrastThreshold = fabs(convolution) * marginRatio;
184  pix.setContrastThreshold(contrastThreshold, *m_me);
185 
186  if (vpDEBUG_ENABLE(3)) {
188  }
189  m_meList.push_back(pix);
190  }
191  }
192  is += stepi;
193  js += stepj;
194  }
195 }
196 
197 void vpMeLine::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness)
198 {
199  vpMeLine::displayLine(I, m_PExt[0], m_PExt[1], m_meList, m_a, m_b, m_c, color, thickness);
200 }
201 
202 void vpMeLine::display(const vpImage<vpRGBa> &I, const vpColor &color, unsigned int thickness)
203 {
204  vpMeLine::displayLine(I, m_PExt[0], m_PExt[1], m_meList, m_a, m_b, m_c, color, thickness);
205 }
206 
208 {
209  vpImagePoint ip1, ip2;
210 
211  vpDisplay::flush(I);
212 
213  std::cout << "Click on the line first point..." << std::endl;
214  while (vpDisplay::getClick(I, ip1) != true) { }
215 
217  vpDisplay::flush(I);
218  std::cout << "Click on the line second point..." << std::endl;
219  while (vpDisplay::getClick(I, ip2) != true) { }
220 
222  vpDisplay::flush(I);
223 
224  initTracking(I, ip1, ip2);
225 }
226 
228 {
229  const unsigned int nos = numberOfSignal(); // number of MEs correctly tracked
230 
231  if (m_meList.size() <= 2 || nos <= 2) {
233  }
234 
235  const double nbr = I.getHeight() / 2.;
236  const double nbc = I.getWidth() / 2.;
237  unsigned int k = 0; // number of good points (should be numberOfSignal
238  // at the end of the first loop and then all after)
239 
240  // System A x = 0 to be solved by least squares
241  // with A = (u v 1) and x = (au, bu, cu)
242 
243  // Note that the (nos-k) last rows of A, xp and yp are not used.
244  // Hopefully, this is not an issue.
245  vpMatrix A(nos, 3);
246 
247  // Useful to compute the weights in the robust estimation
248  vpColVector xp(nos), yp(nos);
249  std::list<vpMeSite>::const_iterator end = m_meList.end();
250 
251  for (std::list<vpMeSite>::const_iterator it = m_meList.begin(); it != end; ++it) {
252  vpMeSite p_me = *it;
253  if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
254  // From (i,j) to (u,v) frame so that (u,v) in [-1;1]
255  double u = (p_me.m_ifloat - nbr) / nbr;
256  double v = (p_me.m_jfloat - nbc) / nbc;
257  A[k][0] = u;
258  A[k][1] = v;
259  A[k][2] = 1.0;
260  // Useful to compute the weights in the robust estimation
261  xp[k] = p_me.m_ifloat;
262  yp[k] = p_me.m_jfloat;
263  ++k;
264  }
265  }
266 
267  const unsigned int minRequiredNbMe = 2;
268  if (k < minRequiredNbMe) {
269  throw(vpException(vpException::dimensionError, "Not enough moving edges %d / %d to track the line ",
270  k, m_meList.size()));
271  }
272 
273  vpRobust r;
274  r.setMinMedianAbsoluteDeviation(1.0); // Image noise in pixels for the distance from points to line
275 
276  unsigned int iter = 0;
277  double var = 10.0;
278  vpColVector x(3);
279  vpMatrix DA(k, 3);
280  vpMatrix KerDA;
281  vpColVector w(k);
282  w = 1.0;
283  vpColVector x_prev(3);
284  x_prev = 0.0;
285 
286  // stop after 4 it or if variation between 2 it is less than 1 pixel
287  const unsigned int maxNbIter = 4;
288  const unsigned int widthDA = DA.getCols();
289  while ((iter < maxNbIter) && (var > 0.1)) {
290  for (unsigned int i = 0; i < k; ++i) {
291  for (unsigned int j = 0; j < widthDA; ++j) {
292  DA[i][j] = w[i] * A[i][j];
293  }
294  }
295  unsigned int dim = DA.nullSpace(KerDA, 1);
296  if (dim > 1) { // case with less than 2 independent points
297  throw(vpMatrixException(vpMatrixException::rankDeficient, "Linear system for computing the line equation ill conditioned"));
298  }
299 
300  for (unsigned int i = 0; i < 3; ++i) {
301  x[i] = KerDA[i][0]; // norm(x) = 1
302  }
303 
304  // using inverse normalization to go back to pixel values
305  double a, b, c;
306  a = x[0]/nbr;
307  b = x[1]/nbc;
308  c = x[2] - x[0] - x[1];
309  // normalization such that a^2+b^2 = 1
310  // important to compute the distance between points and line
311  const double norm = 1.0/sqrt(vpMath::sqr(a)+vpMath::sqr(b));
312  x[0] = a * norm;
313  x[1] = b * norm;
314  x[2] = c * norm;
315 
316  var = (x - x_prev).frobeniusNorm();
317  x_prev = x;
318 
319  vpColVector residu(k); // distance from points to line
320  for (unsigned int i = 0; i < k; ++i) {
321  residu[i] = x[0] * xp[i] + x[1] * yp[i] + x[2];
322  }
323  r.MEstimator(vpRobust::TUKEY, residu, w);
324 
325  ++iter;
326  }
327  m_a = x[0];
328  m_b = x[1];
329  m_c = x[2];
330 
331  // remove all bad points in the list
332  unsigned int i = 0;
333  end = m_meList.end();
334  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != end;) {
335  vpMeSite p_me = *it;
336  if (p_me.getState() != vpMeSite::NO_SUPPRESSION) {
337  it = m_meList.erase(it);
338  }
339  else {
340  // remove outliers
341  if (w[i] < 0.2) { // FS: m_thresholdWeight pour vpMeEllipse
342  it = m_meList.erase(it);
343  }
344  else { // good point
345  ++it;
346  }
347  ++i;
348  }
349  }
350 
351  // Update delta
352  m_delta = atan2(m_a, m_b);
353 
355 }
356 
358 {
359  // 1. We do what concerns straight lines
360  // Extremity points
361  double id1, jd1, id2, jd2;
362  id1 = ip1.get_i();
363  jd1 = ip1.get_j();
364  id2 = ip2.get_i();
365  jd2 = ip2.get_j();
366 
367  m_PExt[0].m_ifloat = id1;
368  m_PExt[0].m_jfloat = jd1;
369  m_PExt[1].m_ifloat = id2;
370  m_PExt[1].m_jfloat = jd2;
371 
372  computeDelta(m_delta, id1, jd1, id2, jd2);
373  m_delta_1 = m_delta;
374 
375  sample(I);
376 
377  // 2. We call what is not specific
379 }
380 
382 {
383  m_PExt[0] = m_meList.front();
384  m_PExt[1] = m_meList.back();
385 }
386 
388 {
389  if (!m_me) {
390  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
391  }
392 
393  if (std::fabs(m_me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
394  throw(vpTrackingException(vpTrackingException::fatalError, "vpMeLine::plugHoles() called with sample step = 0"));
395  }
396 
397  int nbrows = static_cast<int>(I.getHeight());
398  int nbcols = static_cast<int>(I.getWidth());
399 
400  unsigned int memory_range = m_me->getRange();
401  m_me->setRange(1);
402 
403  const double marginRatio = m_me->getThresholdMarginRatio();
404 
405  unsigned int nb_added_points = 0;
406 
407  std::list<vpMeSite>::iterator meList = m_meList.begin();
408  std::list<vpMeSite>::const_iterator end = m_meList.end();
409 
410  vpImagePoint ip1, ip2;
411  getExtremities(ip1, ip2);
412  // To add as many points as possible
413  // i, j portions of the line_p
414  const double sampleStep = fabs(m_me->getSampleStep());
415  double diffsi = m_PExt[1].m_ifloat - m_PExt[0].m_ifloat;
416  double diffsj = m_PExt[1].m_jfloat - m_PExt[0].m_jfloat;
417  double length = sqrt((vpMath::sqr(diffsi) + vpMath::sqr(diffsj)));
418  double stepi = diffsi * sampleStep / length;
419  double stepj = diffsj * sampleStep / length;
420 
421  vpMeSite pix1 = *meList;
422  project(m_a, m_b, m_c, pix1, ip1);
423  ++meList;
424  while (meList != end) {
425  vpMeSite pix2 = *meList;
426  project(m_a, m_b, m_c, pix2, ip2);
427 
428  double dist = sqrt(vpMath::sqr(ip1.get_i() - ip2.get_i())
429  + vpMath::sqr(ip1.get_j() - ip2.get_j()));
430  const unsigned int n_sample = static_cast<unsigned int>(floor(dist / sampleStep));
431  if (n_sample > 1) {
432  double is = ip1.get_i();
433  double js = ip1.get_j();
434  for (unsigned int i = 0; i<n_sample; ++i) {
435  vpImagePoint iP;
436  iP.set_ij(is, js);
437  if (!outOfImage(iP, 5, nbrows, nbcols)) {
438  unsigned int is_uint = static_cast<unsigned int>(is);
439  unsigned int js_uint = static_cast<unsigned int>(js);
440  if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
441  vpMeSite pix;
442  pix.init(is, js, m_delta, 0, m_sign);
445  double convolution = pix.convolution(I, m_me);
446  double contrastThreshold = fabs(convolution) * marginRatio;
447  pix.setContrastThreshold(contrastThreshold, *m_me);
448  pix.track(I, m_me, false);
449  if (pix.getState() == vpMeSite::NO_SUPPRESSION) { // good point
450  nb_added_points++;
451  m_meList.insert(meList, pix);
452  }
453  if (vpDEBUG_ENABLE(3)) {
455  }
456  }
457  }
458  is += stepi;
459  js += stepj;
460  }
461  }
462  pix1 = pix2;
463  ip1 = ip2;
464  ++meList;
465  }
466  m_me->setRange(memory_range);
467 
468  return(nb_added_points);
469 }
470 
472 {
473  int nbrows = static_cast<int>(I.getHeight());
474  int nbcols = static_cast<int>(I.getWidth());
475 
476  // Point extremities strictly on the straight line
477  vpImagePoint ip1, ip2;
478  getExtremities(ip1, ip2);
479  double id1 = ip1.get_i();
480  double jd1 = ip1.get_j();
481  double id2 = ip2.get_i();
482  double jd2 = ip2.get_j();
483 
484  // i, j portions of the line_p
485  double diffsi = id2 - id1;
486  double diffsj = jd2 - jd1;
487  double s = sqrt(vpMath::sqr(diffsi) + vpMath::sqr(diffsj));
488 
489  double sample_step = (double)m_me->getSampleStep();
490 
491  double di = diffsi * sample_step / s; // pas de risque de /0 car d(P1,P2) >0
492  double dj = diffsj * sample_step / s;
493 
494  vpMeSite P;
495 
496  P.init((int)id1, (int)jd1, m_delta_1, 0, m_sign);
498  const double marginRatio = m_me->getThresholdMarginRatio();
499 
500  unsigned int memory_range = m_me->getRange();
501  m_me->setRange(1);
502 
503  unsigned int nb_added_points = 0;
504 
505  // Try to add at max 3 points along first extremity
506  // (could be a little bit more or less)
507  for (int i = 0; i < 3; i++) {
508  id1 -= di;
509  jd1 -= dj;
510  vpImagePoint iP;
511  iP.set_ij(id1, jd1);
512 
513  // First test to ensure that iP coordinates are > 0 before casting to unsigned int
514  if (!outOfImage(iP, 5, nbrows, nbcols)) {
515  unsigned int is_uint = static_cast<unsigned int>(id1);
516  unsigned int js_uint = static_cast<unsigned int>(jd1);
517  // Note here that it is more efficient to cast the coordinates to unsigned int instead of using
518  // directly the image point iP that contains floating point coordinates.
519  // It allows to makes less tests.
520  if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
521  // ajout
522  P.m_ifloat = id1;
523  P.m_i = static_cast<int>(id1);
524  P.m_jfloat = jd1;
525  P.m_j = static_cast<int>(jd1);
526  double convolution = P.convolution(I, m_me);
527  double contrastThreshold = fabs(convolution) * marginRatio;
528  P.setContrastThreshold(contrastThreshold, *m_me);
529  // fin ajout
530  P.track(I, m_me, false);
531  if (P.getState() == vpMeSite::NO_SUPPRESSION) {
532  m_meList.push_front(P);
533  nb_added_points++;
534  if (vpDEBUG_ENABLE(3)) {
536  }
537  }
538  else {
539  if (vpDEBUG_ENABLE(3)) {
541  }
542  }
543  }
544  }
545  }
546 
547  // Try to add at max 3 points along second extremity
548  // (could be a little bit more or less)
549 
550  // P.init((int)id2, (int)jd2, m_delta_1, 0, m_sign);
551  // P.setDisplay(m_selectDisplay);
552 
553  for (int i = 0; i < 3; i++) {
554  id2 += di;
555  jd2 += dj;
556 
557  vpImagePoint iP;
558  iP.set_i(id2);
559  iP.set_j(jd2);
560 
561  // First test to ensure that iP coordinates are > 0 before casting to unsigned int
562  if (!outOfImage(iP, 5, nbrows, nbcols)) {
563  unsigned int is_uint = static_cast<unsigned int>(id2);
564  unsigned int js_uint = static_cast<unsigned int>(jd2);
565  if (inRoiMask(m_mask, is_uint, js_uint) && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
566  // ajout
567  P.m_ifloat = id2;
568  P.m_i = static_cast<int>(id2);
569  P.m_jfloat = jd2;
570  P.m_j = static_cast<int>(jd2);
571  double convolution = P.convolution(I, m_me);
572  double contrastThreshold = fabs(convolution) * marginRatio;
573  P.setContrastThreshold(contrastThreshold, *m_me);
574  // fin ajout
575  P.track(I, m_me, false);
576  if (P.getState() == vpMeSite::NO_SUPPRESSION) {
577  m_meList.push_back(P);
578  nb_added_points++;
579  if (vpDEBUG_ENABLE(3)) {
581  }
582  }
583  else {
584  if (vpDEBUG_ENABLE(3)) {
586  }
587  }
588  }
589  }
590  }
591 
592  m_me->setRange(memory_range);
593  return(nb_added_points);
594 }
595 
597 {
598  if (!m_me) {
599  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
600  }
601 
602  // Good points are in m_meList
603  const unsigned int minNbGoodPoints = 4;
604  if (m_meList.size() <= minNbGoodPoints) {
605 
606  double delta_new = m_delta;
607  m_delta = m_delta_1;
608 
609  vpImagePoint ip;
610  const vpMeSite PExt0 = m_PExt[0];
611  project(m_a, m_b, m_c, PExt0, ip);
612  m_PExt[0].m_ifloat = ip.get_i();
613  m_PExt[0].m_jfloat = ip.get_j();
614 
615  const vpMeSite PExt1 = m_PExt[1];
616  project(m_a, m_b, m_c, PExt1, ip);
617  m_PExt[1].m_ifloat = ip.get_i();
618  m_PExt[1].m_jfloat = ip.get_j();
619 
620  sample(I);
621  m_delta = delta_new;
622 
623  // We call what is not specific
625  // Tracking after resampling
627 
628  // Least square estimation of the straight line parameters
629  leastSquare(I);
630 
631  setExtremities(); // useful if a resampling a getExtremities are done
632 
633  // Updates the delta angle for each point in the list
634  updateDelta();
635 
636  // Remise a jour de delta dans la liste de site me
637  if (vpDEBUG_ENABLE(2)) {
638  display(I, vpColor::red);
640  vpDisplay::flush(I);
641  }
642 
643  computeRhoTheta(I);
644  }
645 }
646 
648 {
649  vpMeSite p_me;
650 
651  double angle_ = m_delta + M_PI / 2;
652  double diff = 0;
653 
654  // angle in [0;180]
655  while (angle_ < 0)
656  angle_ += M_PI;
657  while (angle_ > M_PI)
658  angle_ -= M_PI;
659 
660  // angle in degree
661  angle_ = vpMath::round(angle_ * 180.0 / M_PI);
662  int angle_int = (int)angle_;
663  if (angle_int == 180) angle_ = 179.0;
664 
665 
666  diff = fabs(angle_ - m_angle_1);
667  if (diff > 90) { // To ensure continuity ?
668  m_sign *= -1;
669  }
670 
671  m_angle_1 = angle_;
672 
673  std::list<vpMeSite>::const_iterator end = m_meList.end();
674  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != end; ++it) {
675  p_me = *it;
676  p_me.setAlpha(m_delta);
677  p_me.m_mask_sign = m_sign;
678  *it = p_me;
679  }
680  m_delta_1 = m_delta;
681 }
682 
684 {
685  vpCDEBUG(1) << "begin vpMeLine::track()" << std::endl;
686 
688 
689  // 3. Back to straight lines
690 
691  // Least squares estimation of the parameters of the line
692  leastSquare(I);
693 
694  // Try adding adding new points in holes
695  unsigned int nb = plugHoles(I);
696  // Try adding adding new points at both extremities
697  nb += seekExtremities(I);
698  if (nb > 0) {
699  leastSquare(I);
700  }
701  setExtremities(); // useful if a resampling a getExtremities are done
702 
703  // Resampling if necessary
704  reSample(I);
705 
706  // updates the delta angle for each point in the list
707  updateDelta();
708 
709  if (vpDEBUG_ENABLE(2)) {
710  display(I, vpColor::red);
712  vpDisplay::flush(I);
713  }
714 
715  computeRhoTheta(I);
716 }
717 
718 void vpMeLine::update_indices(double theta, int i, int j, int incr, int &i1, int &i2, int &j1, int &j2)
719 {
720  i1 = (int)(i + cos(theta) * incr);
721  j1 = (int)(j + sin(theta) * incr);
722 
723  i2 = (int)(i - cos(theta) * incr);
724  j2 = (int)(j - sin(theta) * incr);
725 }
726 
728 {
729  m_rho = fabs(m_c);
730  m_theta = atan2(m_b, m_a);
731 
732  // angle in [0;180[
733  while (m_theta >= M_PI) // FC utile uniquement si teta = PI
734  m_theta -= M_PI;
735  while (m_theta < 0)
736  m_theta += M_PI;
737 
738  if (m_useIntensityForRho) {
739  // convention pour choisir le signe de rho
740  int i, j;
741  i = vpMath::round((m_PExt[0].m_ifloat + m_PExt[1].m_ifloat) / 2.0);
742  j = vpMath::round((m_PExt[0].m_jfloat + m_PExt[1].m_jfloat) / 2.0);
743 
744  int end = false;
745  int incr = 10;
746 
747  int i1 = 0, i2 = 0, j1 = 0, j2 = 0;
748  unsigned char v1 = 0, v2 = 0;
749 
750  int width_ = (int)I.getWidth();
751  int height_ = (int)I.getHeight();
752  update_indices(m_theta, i, j, incr, i1, i2, j1, j2);
753 
754  if (i1 < 0 || i1 >= height_ || i2 < 0 || i2 >= height_ || j1 < 0 || j1 >= width_ || j2 < 0 || j2 >= width_) {
755  double rho_lim1 = fabs((double)i / cos(m_theta));
756  double rho_lim2 = fabs((double)j / sin(m_theta));
757 
758  double co_rho_lim1 = fabs(((double)(height_ - i)) / cos(m_theta));
759  double co_rho_lim2 = fabs(((double)(width_ - j)) / sin(m_theta));
760 
761  double rho_lim = std::min<double>(rho_lim1, rho_lim2);
762  double co_rho_lim = std::min<double>(co_rho_lim1, co_rho_lim2);
763  incr = (int)std::floor(std::min<double>(rho_lim, co_rho_lim));
764  if (incr < INCR_MIN) {
765  vpERROR_TRACE("increment is too small");
766  throw(vpTrackingException(vpTrackingException::fatalError, "increment is too small"));
767  }
768  update_indices(m_theta, i, j, incr, i1, i2, j1, j2);
769  }
770 
771  while (!end) {
772  end = true;
773  unsigned int i1_ = static_cast<unsigned int>(i1);
774  unsigned int j1_ = static_cast<unsigned int>(j1);
775  unsigned int i2_ = static_cast<unsigned int>(i2);
776  unsigned int j2_ = static_cast<unsigned int>(j2);
777  v1 = I[i1_][j1_];
778  v2 = I[i2_][j2_];
779  if (abs(v1 - v2) < 1) {
780  incr--;
781  end = false;
782  if (incr == 1) {
783  throw(vpException(vpException::fatalError, "In vpMeLine cannot determine rho sign, since "
784  "there is no grey level difference between both "
785  "sides of the line"));
786  }
787  }
788  update_indices(m_theta, i, j, incr, i1, i2, j1, j2);
789  }
790 
791  if (m_theta >= 0 && m_theta <= M_PI / 2) {
792  if (v2 < v1) {
793  m_theta += M_PI;
794  m_rho *= -1;
795  }
796  }
797 
798  else {
799  double jinter;
800  jinter = -m_c / m_b;
801  if (v2 < v1) {
802  m_theta += M_PI;
803  if (jinter > 0) {
804  m_rho *= -1;
805  }
806  }
807 
808  else {
809  if (jinter < 0) {
810  m_rho *= -1;
811  }
812  }
813  }
814 
815  if (vpDEBUG_ENABLE(2)) {
816  vpImagePoint ip, ip1, ip2;
817 
818  ip.set_i(i);
819  ip.set_j(j);
820  ip1.set_i(i1);
821  ip1.set_j(j1);
822  ip2.set_i(i2);
823  ip2.set_j(j2);
824 
827  }
828  }
829 }
830 
832 {
833  /* Return the coordinates of the two extremities of the line*/
834 
835  const vpMeSite P0 = m_meList.front();
836  project(m_a, m_b, m_c, P0, ip1);
837 
838  const vpMeSite P1 = m_meList.back();
839  project(m_a, m_b, m_c, P1, ip2);
840 }
841 
842 bool vpMeLine::intersection(const vpMeLine &line1, const vpMeLine &line2, vpImagePoint &iP)
843 {
844  double a1 = line1.m_a;
845  double b1 = line1.m_b;
846  double c1 = line1.m_c;
847  double a2 = line2.m_a;
848  double b2 = line2.m_b;
849  double c2 = line2.m_c;
850 
851  double det = a1*b2 - a2*b1;
852 
853  if (std::fabs(det) <= std::numeric_limits<double>::epsilon()) {
854  std::cout << "!!!!!!!!!!!!! Problem : Lines are parallel !!!!!!!!!!!!!" << std::endl;
855  return false;
856  }
857  double i = (c2*b1 - c1*b2)/det;
858  double j = (a2*c1 - a1*c2)/det;
859 
860  iP.set_i(i);
861  iP.set_j(j);
862  return true;
863 }
864 
865 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
880 void vpMeLine::display(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
881  const double &B, const double &C, const vpColor &color, unsigned int thickness)
882 {
883  vpMeLine::displayLine(I, PExt1, PExt2, A, B, C, color, thickness);
884 }
885 
900 void vpMeLine::display(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
901  const double &B, const double &C, const vpColor &color, unsigned int thickness)
902 {
903  vpMeLine::displayLine(I, PExt1, PExt2, A, B, C, color, thickness);
904 }
905 
921 void vpMeLine::display(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
922  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
923  const vpColor &color, unsigned int thickness)
924 {
925  vpMeLine::displayLine(I, PExt1, PExt2, site_list, A, B, C, color, thickness);
926 }
927 
943 void vpMeLine::display(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
944  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
945  const vpColor &color, unsigned int thickness)
946 {
947 
948  vpMeLine::displayLine(I, PExt1, PExt2, site_list, A, B, C, color, thickness);
949 }
950 #endif // Deprecated
951 
952 
953 void vpMeLine::displayLine(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
954  const double &B, const double &C, const vpColor &color, unsigned int thickness)
955 {
956  vpImagePoint ip1, ip2;
957 
958  if (fabs(A) < fabs(B)) {
959  double i1, j1, i2, j2;
960  i1 = 0;
961  j1 = (-A * i1 - C) / B;
962  i2 = I.getHeight() - 1.0;
963  j2 = (-A * i2 - C) / B;
964 
965  ip1.set_i(i1);
966  ip1.set_j(j1);
967  ip2.set_i(i2);
968  ip2.set_j(j2);
969  vpDisplay::displayLine(I, ip1, ip2, color);
970  }
971  else {
972  double i1, j1, i2, j2;
973  j1 = 0;
974  i1 = -(B * j1 + C) / A;
975  j2 = I.getWidth() - 1.0;
976  i2 = -(B * j2 + C) / A;
977 
978  ip1.set_i(i1);
979  ip1.set_j(j1);
980  ip2.set_i(i2);
981  ip2.set_j(j2);
982  vpDisplay::displayLine(I, ip1, ip2, color);
983  }
984 
985  project(A, B, C, PExt1, ip1);
986  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
987 
988  project(A, B, C, PExt2, ip1);
989  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
990 }
991 
992 void vpMeLine::displayLine(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
993  const double &B, const double &C, const vpColor &color, unsigned int thickness)
994 {
995  vpImagePoint ip1, ip2;
996 
997  if (fabs(A) < fabs(B)) {
998  double i1, j1, i2, j2;
999  i1 = 0;
1000  j1 = (-A * i1 - C) / B;
1001  i2 = I.getHeight() - 1.0;
1002  j2 = (-A * i2 - C) / B;
1003 
1004  ip1.set_i(i1);
1005  ip1.set_j(j1);
1006  ip2.set_i(i2);
1007  ip2.set_j(j2);
1008  vpDisplay::displayLine(I, ip1, ip2, color);
1009  }
1010  else {
1011  double i1, j1, i2, j2;
1012  j1 = 0;
1013  i1 = -(B * j1 + C) / A;
1014  j2 = I.getWidth() - 1.0;
1015  i2 = -(B * j2 + C) / A;
1016 
1017  ip1.set_i(i1);
1018  ip1.set_j(j1);
1019  ip2.set_i(i2);
1020  ip2.set_j(j2);
1021  vpDisplay::displayLine(I, ip1, ip2, color);
1022  }
1023  project(A, B, C, PExt1, ip1);
1024  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1025 
1026  project(A, B, C, PExt2, ip1);
1027  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1028 }
1029 
1030 void vpMeLine::displayLine(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
1031  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
1032  const vpColor &color, unsigned int thickness)
1033 {
1034  vpImagePoint ip;
1035  std::list<vpMeSite>::const_iterator end = site_list.end();
1036 
1037  for (std::list<vpMeSite>::const_iterator it = site_list.begin(); it != end; ++it) {
1038  vpMeSite pix = *it;
1039  ip.set_i(pix.m_ifloat);
1040  ip.set_j(pix.m_jfloat);
1041 
1042  if (pix.getState() == vpMeSite::M_ESTIMATOR)
1043  vpDisplay::displayCross(I, ip, 5, vpColor::green, thickness);
1044  else
1045  vpDisplay::displayCross(I, ip, 5, color, thickness);
1046  }
1047 
1048  vpImagePoint ip1, ip2;
1049 
1050  if (fabs(A) < fabs(B)) {
1051  double i1, j1, i2, j2;
1052  i1 = 0;
1053  j1 = (-A * i1 - C) / B;
1054  i2 = I.getHeight() - 1.0;
1055  j2 = (-A * i2 - C) / B;
1056 
1057  ip1.set_i(i1);
1058  ip1.set_j(j1);
1059  ip2.set_i(i2);
1060  ip2.set_j(j2);
1061  vpDisplay::displayLine(I, ip1, ip2, color);
1062  }
1063  else {
1064  double i1, j1, i2, j2;
1065  j1 = 0;
1066  i1 = -(B * j1 + C) / A;
1067  j2 = I.getWidth() - 1.0;
1068  i2 = -(B * j2 + C) / A;
1069 
1070  ip1.set_i(i1);
1071  ip1.set_j(j1);
1072  ip2.set_i(i2);
1073  ip2.set_j(j2);
1074  vpDisplay::displayLine(I, ip1, ip2, color);
1075  }
1076  project(A, B, C, PExt1, ip1);
1077  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1078 
1079  project(A, B, C, PExt2, ip1);
1080  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1081 }
1082 
1083 void vpMeLine::displayLine(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
1084  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
1085  const vpColor &color, unsigned int thickness)
1086 {
1087  vpImagePoint ip;
1088  std::list<vpMeSite>::const_iterator end = site_list.end();
1089 
1090  for (std::list<vpMeSite>::const_iterator it = site_list.begin(); it != end; ++it) {
1091  vpMeSite pix = *it;
1092  ip.set_i(pix.m_ifloat);
1093  ip.set_j(pix.m_jfloat);
1094 
1095  if (pix.getState() == vpMeSite::M_ESTIMATOR)
1096  vpDisplay::displayCross(I, ip, 5, vpColor::green, thickness);
1097  else
1098  vpDisplay::displayCross(I, ip, 5, color, thickness);
1099  }
1100 
1101  vpImagePoint ip1, ip2;
1102 
1103  if (fabs(A) < fabs(B)) {
1104  double i1, j1, i2, j2;
1105  i1 = 0;
1106  j1 = (-A * i1 - C) / B;
1107  i2 = I.getHeight() - 1.0;
1108  j2 = (-A * i2 - C) / B;
1109 
1110  ip1.set_i(i1);
1111  ip1.set_j(j1);
1112  ip2.set_i(i2);
1113  ip2.set_j(j2);
1114  vpDisplay::displayLine(I, ip1, ip2, color);
1115  }
1116  else {
1117  double i1, j1, i2, j2;
1118  j1 = 0;
1119  i1 = -(B * j1 + C) / A;
1120  j2 = I.getWidth() - 1.0;
1121  i2 = -(B * j2 + C) / A;
1122 
1123  ip1.set_i(i1);
1124  ip1.set_j(j1);
1125  ip2.set_i(i2);
1126  ip2.set_j(j2);
1127  vpDisplay::displayLine(I, ip1, ip2, color);
1128  }
1129  project(A, B, C, PExt1, ip1);
1130  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1131 
1132  project(A, B, C, PExt2, ip1);
1133  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1134 }
1135 END_VISP_NAMESPACE
unsigned int getCols() const
Definition: vpArray2D.h:337
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
Class to define RGB colors available for display functionalities.
Definition: vpColor.h:157
static const vpColor red
Definition: vpColor.h:198
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 displayLine(const vpImage< unsigned char > &I, const vpImagePoint &ip1, const vpImagePoint &ip2, const vpColor &color, unsigned int thickness=1, bool segment=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 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
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ 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
void set_j(double jj)
Definition: vpImagePoint.h:309
double get_j() const
Definition: vpImagePoint.h:125
void set_ij(double ii, double jj)
Definition: vpImagePoint.h:320
void set_i(double ii)
Definition: vpImagePoint.h:298
double get_i() const
Definition: vpImagePoint.h:114
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getHeight() const
Definition: vpImage.h:181
static double sqr(double x)
Definition: vpMath.h:203
static int round(double x)
Definition: vpMath.h:410
error that can be emitted by the vpMatrix class and its derivatives
@ rankDeficient
Rank deficient.
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
unsigned int nullSpace(vpMatrix &kerA, double svThreshold=1e-6) const
Definition: vpMatrix.cpp:1487
Class that tracks in an image a line moving edges.
Definition: vpMeLine.h:153
double m_angle
Angle in deg between the extremities.
Definition: vpMeLine.h:478
double m_angle_1
Angle in deg between the extremities.
Definition: vpMeLine.h:479
double m_theta
theta parameter of the line
Definition: vpMeLine.h:475
int m_sign
Sign.
Definition: vpMeLine.h:480
void normalizeAngle(double &delta)
Definition: vpMeLine.cpp:58
double m_delta_1
Angle in rad between the extremities.
Definition: vpMeLine.h:477
unsigned int plugHoles(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:387
static void project(double a, double b, double c, const vpMeSite &P, vpImagePoint &iP)
Definition: vpMeLine.cpp:86
void computeRhoTheta(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:727
void updateDelta()
Definition: vpMeLine.cpp:647
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1)
Definition: vpMeLine.cpp:197
void reSample(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:596
double m_c
Parameter c of the line equation a*i + b*j + c = 0.
Definition: vpMeLine.h:488
void computeDelta(double &delta, double i1, double j1, double i2, double j2)
Definition: vpMeLine.cpp:69
bool m_useIntensityForRho
Definition: vpMeLine.h:484
double m_delta
Angle in rad between the extremities.
Definition: vpMeLine.h:476
double m_rho
rho parameter of the line
Definition: vpMeLine.h:474
void track(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:683
void getExtremities(vpImagePoint &ip1, vpImagePoint &ip2) const
Definition: vpMeLine.cpp:831
static void displayLine(const vpImage< unsigned char > &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A, const double &B, const double &C, const vpColor &color=vpColor::green, unsigned int thickness=1)
Definition: vpMeLine.cpp:953
double m_a
Parameter a of the line equation a*i + b*j + c = 0.
Definition: vpMeLine.h:486
virtual void sample(const vpImage< unsigned char > &I, bool doNotTrack=false) VP_OVERRIDE
Definition: vpMeLine.cpp:131
void leastSquare(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:227
double m_b
Parameter b of the line equation a*i + b*j + c = 0.
Definition: vpMeLine.h:487
vpMeSite m_PExt[2]
Definition: vpMeLine.h:471
void setExtremities()
Definition: vpMeLine.cpp:381
void initTracking(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:207
static bool intersection(const vpMeLine &line1, const vpMeLine &line2, vpImagePoint &iP)
Definition: vpMeLine.cpp:842
virtual ~vpMeLine() VP_OVERRIDE
Definition: vpMeLine.cpp:126
virtual unsigned int seekExtremities(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:471
Performs search in a given direction(normal) for a given distance(pixels) for a given 'site'....
Definition: vpMeSite.h:68
int m_mask_sign
Mask sign.
Definition: vpMeSite.h:107
@ M_ESTIMATOR
Point detected as an outlier during virtual visual-servoing.
Definition: vpMeSite.h:92
@ 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
double convolution(const vpImage< unsigned char > &ima, const vpMe *me)
Definition: vpMeSite.cpp:227
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 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
void setContrastThreshold(const double &thresh, const vpMe &me)
Definition: vpMeSite.h:303
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)
const vpImage< bool > * m_mask
Mask used to disable tracking on a part of image.
Definition: vpMeTracker.h:319
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
static bool inRoiMask(const vpImage< bool > *mask, unsigned int i, unsigned int j)
void display(const vpImage< unsigned char > &I)
bool outOfImage(int i, int j, int border, int nrows, int ncols)
const vpImage< bool > * m_maskCandidates
Mask used to determine candidate points for initialization in an image.
Definition: vpMeTracker.h:321
std::list< vpMeSite > m_meList
Definition: vpMeTracker.h:311
static bool inMeMaskCandidates(const vpImage< bool > *meMaskCandidates, unsigned int i, unsigned int j)
void setRange(const unsigned int &range)
Definition: vpMe.h:415
double getThresholdMarginRatio() const
Definition: vpMe.h:300
double getSampleStep() const
Definition: vpMe.h:275
unsigned int getRange() const
Definition: vpMe.h:268
Contains an M-estimator and various influence function.
Definition: vpRobust.h:84
@ TUKEY
Tukey influence function.
Definition: vpRobust.h:89
void MEstimator(const vpRobustEstimatorType method, const vpColVector &residues, vpColVector &weights)
Definition: vpRobust.cpp:130
void setMinMedianAbsoluteDeviation(double mad_min)
Definition: vpRobust.h:136
Error that can be emitted by the vpTracker class and its derivatives.
@ notEnoughPointError
Not enough point to track.
@ initializationError
Tracker initialization error.
@ fatalError
Tracker fatal error.