Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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/me/vpMe.h>
49 #include <visp3/me/vpMeLine.h>
50 #include <visp3/me/vpMeSite.h>
51 #include <visp3/me/vpMeTracker.h>
52 
53 #define INCR_MIN 1
54 
56 void computeDelta(double &delta, int i1, int j1, int i2, int j2);
57 
58 static void normalizeAngle(double &delta)
59 {
60  while (delta > M_PI) {
61  delta -= M_PI;
62  }
63  while (delta < -M_PI) {
64  delta += M_PI;
65  }
66 }
67 
68 void computeDelta(double &delta, int i1, int j1, int i2, int j2)
69 {
70 
71  double B = double(i1 - i2);
72  double A = double(j1 - j2);
73 
74  delta = atan2(B, A);
75  delta -= M_PI / 2.0;
76  normalizeAngle(delta);
77 }
78 
79 static void project(double a, double b, double c, double i, double j, double &ip, double &jp)
80 {
81  if (fabs(a) > fabs(b)) {
82  jp = (vpMath::sqr(a) * j - a * b * i - c * b) / (vpMath::sqr(a) + vpMath::sqr(b));
83  ip = (-c - b * jp) / a;
84  }
85  else {
86  ip = (vpMath::sqr(b) * i - a * b * j - c * a) / (vpMath::sqr(a) + vpMath::sqr(b));
87  jp = (-c - a * ip) / b;
88  }
89 }
90 
92  : m_rho(0.), m_theta(0.), m_delta(0.), m_delta_1(0.), m_angle(0.), m_angle_1(90), m_sign(1),
93  m_useIntensityForRho(true), m_a(0.), m_b(0.), m_c(0.)
94 { }
95 
97  : 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),
98  m_useIntensityForRho(true), m_a(0.), m_b(0.), m_c(0.)
99 
100 {
101  m_rho = meline.m_rho;
102  m_theta = meline.m_theta;
103  m_delta = meline.m_delta;
104  m_delta_1 = meline.m_delta_1;
105  m_angle = meline.m_angle;
106  m_angle_1 = meline.m_angle_1;
107  m_sign = meline.m_sign;
108 
109  m_a = meline.m_a;
110  m_b = meline.m_b;
111  m_c = meline.m_c;
113  m_PExt[0] = meline.m_PExt[0];
114  m_PExt[1] = meline.m_PExt[1];
115 }
116 
118 {
119  m_meList.clear();
120 }
121 
122 void vpMeLine::sample(const vpImage<unsigned char> &I, bool doNotTrack)
123 {
124  (void)doNotTrack;
125  if (!m_me) {
126  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
127  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
128  }
129 
130  int nbrows = static_cast<int>(I.getHeight());
131  int nbcols = static_cast<int>(I.getWidth());
132  double n_sample;
133 
134  if (std::fabs(m_me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
135  vpERROR_TRACE("function called with sample step = 0");
136  throw(vpTrackingException(vpTrackingException::fatalError, "sample step = 0"));
137  }
138 
139  // i, j portions of the line_p
140  double diffsi = m_PExt[0].m_ifloat - m_PExt[1].m_ifloat;
141  double diffsj = m_PExt[0].m_jfloat - m_PExt[1].m_jfloat;
142 
143  double length_p = sqrt((vpMath::sqr(diffsi) + vpMath::sqr(diffsj)));
144  if (std::fabs(length_p) <= std::numeric_limits<double>::epsilon())
145  throw(vpTrackingException(vpTrackingException::fatalError, "points too close of each other to define a line"));
146  // number of samples along line_p
147  n_sample = length_p / (double)m_me->getSampleStep();
148 
149  double stepi = diffsi / (double)n_sample;
150  double stepj = diffsj / (double)n_sample;
151 
152  // Choose starting point
153  double is = m_PExt[1].m_ifloat;
154  double js = m_PExt[1].m_jfloat;
155 
156  // Delete old list
157  m_meList.clear();
158 
159  // sample positions at i*m_me->getSampleStep() interval along the
160  // line_p, starting at PSiteExt[0]
161 
162  for (int i = 0; i <= vpMath::round(n_sample); i++) {
163  vpImagePoint iP;
164  iP.set_i(is);
165  iP.set_j(js);
166  unsigned int is_uint = static_cast<unsigned int>(is);
167  unsigned int js_uint = static_cast<unsigned int>(js);
168  // If point is in the image, add to the sample list
169  if ((!outOfImage(iP, 0, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)
170  && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
171  vpMeSite pix;
172  pix.init(iP.get_i(), iP.get_j(), m_delta, 0, m_sign);
175  const double marginRatio = m_me->getThresholdMarginRatio();
176  double convolution = pix.convolution(I, m_me);
177  double contrastThreshold = fabs(convolution) * marginRatio;
178  pix.setContrastThreshold(contrastThreshold, *m_me);
179 
180  if (vpDEBUG_ENABLE(3)) {
182  }
183 
184  m_meList.push_back(pix);
185  }
186  is += stepi;
187  js += stepj;
188  }
189 
190  vpCDEBUG(1) << "end vpMeLine::sample() : ";
191  vpCDEBUG(1) << n_sample << " point inserted in the list " << std::endl;
192 }
193 
194 void vpMeLine::display(const vpImage<unsigned char> &I, const vpColor &color, unsigned int thickness)
195 {
196  vpMeLine::displayLine(I, m_PExt[0], m_PExt[1], m_meList, m_a, m_b, m_c, color, thickness);
197 }
198 
200 {
201  vpImagePoint ip1, ip2;
202 
203  vpDisplay::flush(I);
204 
205  std::cout << "Click on the line first point..." << std::endl;
206  while (vpDisplay::getClick(I, ip1) != true) { }
207 
209  vpDisplay::flush(I);
210  std::cout << "Click on the line second point..." << std::endl;
211  while (vpDisplay::getClick(I, ip2) != true) { }
212 
214  vpDisplay::flush(I);
215 
216  try {
217  initTracking(I, ip1, ip2);
218  }
219  catch (...) {
220  vpERROR_TRACE("Error caught");
221  throw;
222  }
223 }
224 
226 {
227  vpMatrix A(numberOfSignal(), 2);
228  vpColVector x(2), x_1(2);
229  x_1 = 0;
230 
231  vpRobust r;
234  D.eye();
235  vpMatrix DA(numberOfSignal(), 2);
238  w = 1;
239  vpMeSite p_me;
240  unsigned int iter = 0;
241  unsigned int nos_1 = 0;
242  double distance = 100;
243 
244  if (m_meList.size() <= 2 || numberOfSignal() <= 2) {
245  // vpERROR_TRACE("Not enough point") ;
246  vpCDEBUG(1) << "Not enough point";
248  }
249 
250  if ((fabs(m_b) >= 0.9)) // Construction du systeme Ax=B
251  // a i + j + c = 0
252  // A = (i 1) B = (-j)
253  {
254  nos_1 = numberOfSignal();
255  unsigned int k = 0;
256  for (std::list<vpMeSite>::const_iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
257  p_me = *it;
258  if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
259  A[k][0] = p_me.m_ifloat;
260  A[k][1] = 1;
261  B[k] = -p_me.m_jfloat;
262  k++;
263  }
264  }
265 
266  while (iter < 4 && distance > 0.05) {
267  for (unsigned int i = 0; i < k; i++) {
268  for (unsigned int j = 0; j < 2; j++) {
269  DA[i][j] = w[i] * A[i][j];
270  }
271  }
272 
273  x = DA.pseudoInverse(1e-26) * D * B;
274 
275  vpColVector residu(nos_1);
276  residu = B - A * x;
277  r.MEstimator(vpRobust::TUKEY, residu, w);
278 
279  k = 0;
280  for (unsigned int i = 0; i < nos_1; i++) {
281  D[k][k] = w[k];
282  k++;
283  }
284  iter++;
285  distance = fabs(x[0] - x_1[0]) + fabs(x[1] - x_1[1]);
286  x_1 = x;
287  }
288 
289  k = 0;
290  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
291  p_me = *it;
292  if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
293  if (w[k] < 0.2) {
295 
296  *it = p_me;
297  }
298  k++;
299  }
300  }
301 
302  // mise a jour de l'equation de la droite
303  m_a = x[0];
304  m_b = 1;
305  m_c = x[1];
306 
307  double s = sqrt(vpMath::sqr(m_a) + vpMath::sqr(m_b));
308  m_a /= s;
309  m_b /= s;
310  m_c /= s;
311  }
312 
313  else // Construction du systeme Ax=B
314  // i + bj + c = 0
315  // A = (j 1) B = (-i)
316  {
317  nos_1 = numberOfSignal();
318  unsigned int k = 0;
319  for (std::list<vpMeSite>::const_iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
320  p_me = *it;
321  if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
322  A[k][0] = p_me.m_jfloat;
323  A[k][1] = 1;
324  B[k] = -p_me.m_ifloat;
325  k++;
326  }
327  }
328 
329  while (iter < 4 && distance > 0.05) {
330  DA = D * A;
331  x = DA.pseudoInverse(1e-26) * D * B;
332 
333  vpColVector residu(nos_1);
334  residu = B - A * x;
335  r.MEstimator(vpRobust::TUKEY, residu, w);
336 
337  k = 0;
338  for (unsigned int i = 0; i < nos_1; i++) {
339  D[k][k] = w[k];
340  k++;
341  }
342  iter++;
343  distance = fabs(x[0] - x_1[0]) + fabs(x[1] - x_1[1]);
344  x_1 = x;
345  }
346 
347  k = 0;
348  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
349  p_me = *it;
350  if (p_me.getState() == vpMeSite::NO_SUPPRESSION) {
351  if (w[k] < 0.2) {
353 
354  *it = p_me;
355  }
356  k++;
357  }
358  }
359  m_a = 1;
360  m_b = x[0];
361  m_c = x[1];
362 
363  double s = sqrt(vpMath::sqr(m_a) + vpMath::sqr(m_b));
364  m_a /= s;
365  m_b /= s;
366  m_c /= s;
367  }
368 
369  // mise a jour du delta
370  m_delta = atan2(m_a, m_b);
371 
372  normalizeAngle(m_delta);
373 }
374 
376 {
377  vpCDEBUG(1) << " begin vpMeLine::initTracking()" << std::endl;
378 
379  int i1s, j1s, i2s, j2s;
380 
381  i1s = vpMath::round(ip1.get_i());
382  i2s = vpMath::round(ip2.get_i());
383  j1s = vpMath::round(ip1.get_j());
384  j2s = vpMath::round(ip2.get_j());
385 
386  try {
387 
388  // 1. On fait ce qui concerne les droites (peut etre vide)
389  {
390  // Points extremites
391  m_PExt[0].m_ifloat = (float)ip1.get_i();
392  m_PExt[0].m_jfloat = (float)ip1.get_j();
393  m_PExt[1].m_ifloat = (float)ip2.get_i();
394  m_PExt[1].m_jfloat = (float)ip2.get_j();
395 
396  double angle_ = atan2((double)(i1s - i2s), (double)(j1s - j2s));
397  m_a = cos(angle_);
398  m_b = sin(angle_);
399 
400  // Real values of a, b can have an other sign. So to get the good values
401  // of a and b in order to initialise then c, we call track(I) just below
402 
403  computeDelta(m_delta, i1s, j1s, i2s, j2s);
404  m_delta_1 = m_delta;
405 
406  // vpTRACE("a: %f b: %f c: %f -b/a: %f delta: %f", a, b, c, -(b/a),
407  // delta);
408 
409  sample(I);
410  }
411  // 2. On appelle ce qui n'est pas specifique
412  {
414  }
415  // Call track(I) to give the good sign to a and b and to initialise c
416  // which can be used for the display
417  track(I);
418  }
419  catch (...) {
420  vpERROR_TRACE("Error caught");
421  throw;
422  }
423  vpCDEBUG(1) << " end vpMeLine::initTracking()" << std::endl;
424 }
425 
427 {
428  // Loop through list of sites to track
429  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end();) {
430  vpMeSite s = *it; // current reference pixel
431 
433  it = m_meList.erase(it);
434  else
435  ++it;
436  }
437 }
438 
440 {
441  double imin = +1e6;
442  double jmin = +1e6;
443  double imax = -1;
444  double jmax = -1;
445 
446  // Loop through list of sites to track
447  for (std::list<vpMeSite>::const_iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
448  vpMeSite s = *it; // current reference pixel
449  if (s.m_ifloat < imin) {
450  imin = s.m_ifloat;
451  jmin = s.m_jfloat;
452  }
453 
454  if (s.m_ifloat > imax) {
455  imax = s.m_ifloat;
456  jmax = s.m_jfloat;
457  }
458  }
459 
460  m_PExt[0].m_ifloat = imin;
461  m_PExt[0].m_jfloat = jmin;
462  m_PExt[1].m_ifloat = imax;
463  m_PExt[1].m_jfloat = jmax;
464 
465  if (fabs(imin - imax) < 25) {
466  for (std::list<vpMeSite>::const_iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
467  vpMeSite s = *it; // current reference pixel
468  if (s.m_jfloat < jmin) {
469  imin = s.m_ifloat;
470  jmin = s.m_jfloat;
471  }
472 
473  if (s.m_jfloat > jmax) {
474  imax = s.m_ifloat;
475  jmax = s.m_jfloat;
476  }
477  }
478  m_PExt[0].m_ifloat = imin;
479  m_PExt[0].m_jfloat = jmin;
480  m_PExt[1].m_ifloat = imax;
481  m_PExt[1].m_jfloat = jmax;
482  }
483 }
484 
486 {
487  vpCDEBUG(1) << "begin vpMeLine::sample() : " << std::endl;
488 
489  if (!m_me) {
490  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
491  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
492  }
493 
494  int nbrows = static_cast<int>(I.getHeight());
495  int nbcols = static_cast<int>(I.getWidth());
496  double n_sample;
497 
498  // if (m_me->getSampleStep()==0)
499  if (std::fabs(m_me->getSampleStep()) <= std::numeric_limits<double>::epsilon()) {
500 
501  vpERROR_TRACE("function called with sample step = 0");
502  throw(vpTrackingException(vpTrackingException::fatalError, "sample step = 0"));
503  }
504 
505  // i, j portions of the line_p
506  double diffsi = m_PExt[0].m_ifloat - m_PExt[1].m_ifloat;
507  double diffsj = m_PExt[0].m_jfloat - m_PExt[1].m_jfloat;
508 
509  double s = vpMath::sqr(diffsi) + vpMath::sqr(diffsj);
510 
511  double di = diffsi / sqrt(s); // pas de risque de /0 car d(P1,P2) >0
512  double dj = diffsj / sqrt(s);
513 
514  double length_p = sqrt((vpMath::sqr(diffsi) + vpMath::sqr(diffsj)));
515 
516  // number of samples along line_p
517  n_sample = length_p / (double)m_me->getSampleStep();
518  double sample_step = (double)m_me->getSampleStep();
519 
520  vpMeSite P;
521  P.init((int)m_PExt[0].m_ifloat, (int)m_PExt[0].m_jfloat, m_delta_1, 0, m_sign);
523 
524  unsigned int memory_range = m_me->getRange();
525  m_me->setRange(1);
526 
527 
528  for (int i = 0; i < 3; i++) {
529  P.m_ifloat = P.m_ifloat + di * sample_step;
530  P.m_i = static_cast<int>(P.m_ifloat);
531  P.m_jfloat = P.m_jfloat + dj * sample_step;
532  P.m_j = static_cast<int>(P.m_jfloat);
533 
534  vpImagePoint iP;
535  iP.set_i(P.m_ifloat);
536  iP.set_j(P.m_jfloat);
537 
538  unsigned int is_uint = static_cast<unsigned int>(P.m_ifloat);
539  unsigned int js_uint = static_cast<unsigned int>(P.m_jfloat);
540  if ((!outOfImage(iP, 5, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)
541  && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
542  P.track(I, m_me, false);
543 
544  if (P.getState() == vpMeSite::NO_SUPPRESSION) {
545  m_meList.push_back(P);
546  if (vpDEBUG_ENABLE(3)) {
548  }
549  }
550  else {
551  if (vpDEBUG_ENABLE(3)) {
553  }
554  }
555  }
556  }
557 
558  P.init((int)m_PExt[1].m_ifloat, (int)m_PExt[1].m_jfloat, m_delta_1, 0, m_sign);
560  for (int i = 0; i < 3; i++) {
561  P.m_ifloat = P.m_ifloat - di * sample_step;
562  P.m_i = static_cast<int>(P.m_ifloat);
563  P.m_jfloat = P.m_jfloat - dj * sample_step;
564  P.m_j = static_cast<int>(P.m_jfloat);
565 
566  vpImagePoint iP;
567  iP.set_i(P.m_ifloat);
568  iP.set_j(P.m_jfloat);
569 
570  unsigned int is_uint = static_cast<unsigned int>(P.m_ifloat);
571  unsigned int js_uint = static_cast<unsigned int>(P.m_jfloat);
572  if ((!outOfImage(iP, 5, nbrows, nbcols)) && inRoiMask(m_mask, is_uint, js_uint)
573  && inMeMaskCandidates(m_maskCandidates, is_uint, js_uint)) {
574  P.track(I, m_me, false);
575 
576  if (P.getState() == vpMeSite::NO_SUPPRESSION) {
577  m_meList.push_back(P);
578  if (vpDEBUG_ENABLE(3)) {
580  }
581  }
582  else {
583  if (vpDEBUG_ENABLE(3)) {
585  }
586  }
587  }
588  }
589 
590  m_me->setRange(memory_range);
591 
592  vpCDEBUG(1) << "end vpMeLine::sample() : ";
593  vpCDEBUG(1) << n_sample << " point inserted in the list " << std::endl;
594 }
595 
597 {
598  double i1, j1, i2, j2;
599 
600  if (!m_me) {
601  vpDERROR_TRACE(2, "Tracking error: Moving edges not initialized");
602  throw(vpTrackingException(vpTrackingException::initializationError, "Moving edges not initialized"));
603  }
604 
605  project(m_a, m_b, m_c, m_PExt[0].m_ifloat, m_PExt[0].m_jfloat, i1, j1);
606  project(m_a, m_b, m_c, m_PExt[1].m_ifloat, m_PExt[1].m_jfloat, i2, j2);
607 
608  // Points extremites
609  m_PExt[0].m_ifloat = i1;
610  m_PExt[0].m_jfloat = j1;
611  m_PExt[1].m_ifloat = i2;
612  m_PExt[1].m_jfloat = j2;
613 
614  double d = sqrt(vpMath::sqr(i1 - i2) + vpMath::sqr(j1 - j2));
615 
616  unsigned int n = numberOfSignal();
617  double expecteddensity = d / (double)m_me->getSampleStep();
618 
619  if ((double)n < 0.9 * expecteddensity) {
620  double delta_new = m_delta;
621  m_delta = m_delta_1;
622  sample(I);
623  m_delta = delta_new;
624  // 2. On appelle ce qui n'est pas specifique
626  }
627 }
628 
630 {
631  vpMeSite p_me;
632 
633  double angle_ = m_delta + M_PI / 2;
634  double diff = 0;
635 
636  while (angle_ < 0)
637  angle_ += M_PI;
638  while (angle_ > M_PI)
639  angle_ -= M_PI;
640 
641  angle_ = vpMath::round(angle_ * 180 / M_PI);
642 
643  // if(fabs(angle_) == 180 )
644  if (std::fabs(std::fabs(angle_) - 180) <= std::numeric_limits<double>::epsilon()) {
645  angle_ = 0;
646  }
647 
648  // std::cout << "angle theta : " << theta << std::endl ;
649  diff = fabs(angle_ - m_angle_1);
650  if (diff > 90)
651  m_sign *= -1;
652 
653  m_angle_1 = angle_;
654 
655  for (std::list<vpMeSite>::iterator it = m_meList.begin(); it != m_meList.end(); ++it) {
656  p_me = *it;
657  p_me.setAlpha(m_delta);
658  p_me.m_mask_sign = m_sign;
659  *it = p_me;
660  }
661  m_delta_1 = m_delta;
662 }
663 
665 {
666  vpCDEBUG(1) << "begin vpMeLine::track()" << std::endl;
667 
669 
670  // 3. On revient aux droites
671  {
672  // supression des points rejetes par les ME
673  suppressPoints();
674  setExtremities();
675 
676  // Estimation des parametres de la droite aux moindres carre
677  try {
678  leastSquare();
679  }
680  catch (...) {
681  vpERROR_TRACE("Error caught");
682  throw;
683  }
684 
685  // recherche de point aux extremite de la droites
686  // dans le cas d'un glissement
687  seekExtremities(I);
688 
689  setExtremities();
690  try {
691  leastSquare();
692  }
693  catch (...) {
694  vpERROR_TRACE("Error caught");
695  throw;
696  }
697 
698  // suppression des points rejetes par la regression robuste
699  suppressPoints();
700  setExtremities();
701 
702  // reechantillonage si necessaire
703  reSample(I);
704 
705  // remet a jour l'angle delta pour chaque point de la liste
706 
707  updateDelta();
708 
709  // Remise a jour de delta dans la liste de site me
710  if (vpDEBUG_ENABLE(2)) {
711  display(I, vpColor::red);
713  vpDisplay::flush(I);
714  }
715  }
716 
717  computeRhoTheta(I);
718 
719  vpCDEBUG(1) << "end vpMeLine::track()" << std::endl;
720 }
721 
722 void vpMeLine::update_indices(double theta, int i, int j, int incr, int &i1, int &i2, int &j1, int &j2)
723 {
724  i1 = (int)(i + cos(theta) * incr);
725  j1 = (int)(j + sin(theta) * incr);
726 
727  i2 = (int)(i - cos(theta) * incr);
728  j2 = (int)(j - sin(theta) * incr);
729 }
730 
732 {
733  m_rho = fabs(m_c);
734  m_theta = atan2(m_b, m_a);
735 
736  while (m_theta >= M_PI)
737  m_theta -= M_PI;
738  while (m_theta < 0)
739  m_theta += M_PI;
740 
741  if (m_useIntensityForRho) {
742  // convention pour choisir le signe de rho
743  int i, j;
744  i = vpMath::round((m_PExt[0].m_ifloat + m_PExt[1].m_ifloat) / 2);
745  j = vpMath::round((m_PExt[0].m_jfloat + m_PExt[1].m_jfloat) / 2);
746 
747  int end = false;
748  int incr = 10;
749 
750  int i1 = 0, i2 = 0, j1 = 0, j2 = 0;
751  unsigned char v1 = 0, v2 = 0;
752 
753  int width_ = (int)I.getWidth();
754  int height_ = (int)I.getHeight();
755  update_indices(m_theta, i, j, incr, i1, i2, j1, j2);
756 
757  if (i1 < 0 || i1 >= height_ || i2 < 0 || i2 >= height_ || j1 < 0 || j1 >= width_ || j2 < 0 || j2 >= width_) {
758  double rho_lim1 = fabs((double)i / cos(m_theta));
759  double rho_lim2 = fabs((double)j / sin(m_theta));
760 
761  double co_rho_lim1 = fabs(((double)(height_ - i)) / cos(m_theta));
762  double co_rho_lim2 = fabs(((double)(width_ - j)) / sin(m_theta));
763 
764  double rho_lim = std::min<double>(rho_lim1, rho_lim2);
765  double co_rho_lim = std::min<double>(co_rho_lim1, co_rho_lim2);
766  incr = (int)std::floor(std::min<double>(rho_lim, co_rho_lim));
767  if (incr < INCR_MIN) {
768  vpERROR_TRACE("increment is too small");
769  throw(vpTrackingException(vpTrackingException::fatalError, "increment is too small"));
770  }
771  update_indices(m_theta, i, j, incr, i1, i2, j1, j2);
772  }
773 
774  while (!end) {
775  end = true;
776  unsigned int i1_ = static_cast<unsigned int>(i1);
777  unsigned int j1_ = static_cast<unsigned int>(j1);
778  unsigned int i2_ = static_cast<unsigned int>(i2);
779  unsigned int j2_ = static_cast<unsigned int>(j2);
780  v1 = I[i1_][j1_];
781  v2 = I[i2_][j2_];
782  if (abs(v1 - v2) < 1) {
783  incr--;
784  end = false;
785  if (incr == 1) {
786  throw(vpException(vpException::fatalError, "In vpMeLine cannot determine rho sign, since "
787  "there is no gray level difference between both "
788  "sides of the line"));
789  }
790  }
791  update_indices(m_theta, i, j, incr, i1, i2, j1, j2);
792  }
793 
794  if (m_theta >= 0 && m_theta <= M_PI / 2) {
795  if (v2 < v1) {
796  m_theta += M_PI;
797  m_rho *= -1;
798  }
799  }
800 
801  else {
802  double jinter;
803  jinter = -m_c / m_b;
804  if (v2 < v1) {
805  m_theta += M_PI;
806  if (jinter > 0) {
807  m_rho *= -1;
808  }
809  }
810 
811  else {
812  if (jinter < 0) {
813  m_rho *= -1;
814  }
815  }
816  }
817 
818  if (vpDEBUG_ENABLE(2)) {
819  vpImagePoint ip, ip1, ip2;
820 
821  ip.set_i(i);
822  ip.set_j(j);
823  ip1.set_i(i1);
824  ip1.set_j(j1);
825  ip2.set_i(i2);
826  ip2.set_j(j2);
827 
830  }
831  }
832 }
833 
834 double vpMeLine::getRho() const { return m_rho; }
835 
836 double vpMeLine::getTheta() const { return m_theta; }
837 
839 {
840  /*Return the coordinates of the extremities of the line*/
841  ip1.set_i(m_PExt[0].m_ifloat);
842  ip1.set_j(m_PExt[0].m_jfloat);
843  ip2.set_i(m_PExt[1].m_ifloat);
844  ip2.set_j(m_PExt[1].m_jfloat);
845 }
846 
847 bool vpMeLine::intersection(const vpMeLine &line1, const vpMeLine &line2, vpImagePoint &ip)
848 {
849  double a1 = line1.m_a;
850  double b1 = line1.m_b;
851  double c1 = line1.m_c;
852  double a2 = line2.m_a;
853  double b2 = line2.m_b;
854  double c2 = line2.m_c;
855 
856  try {
857  double i = 0, j = 0;
858  double denom = 0;
859 
860  if (a1 > 0.1) {
861  denom = (-(a2 / a1) * b1 + b2);
862 
863  // if (denom == 0)
864  if (std::fabs(denom) <= std::numeric_limits<double>::epsilon()) {
865  std::cout << "!!!!!!!!!!!!! Problem : Lines are parallel !!!!!!!!!!!!!" << std::endl;
866  return (false);
867  }
868 
869  // if (denom != 0 )
870  if (std::fabs(denom) > std::numeric_limits<double>::epsilon()) {
871  j = ((a2 / a1) * c1 - c2) / denom;
872  i = (-b1 * j - c1) / a1;
873  }
874  }
875 
876  else {
877  denom = (-(b2 / b1) * a1 + a2);
878 
879  // if (denom == 0)
880  if (std::fabs(denom) <= std::numeric_limits<double>::epsilon()) {
881  std::cout << "!!!!!!!!!!!!! Problem : Lines are parallel !!!!!!!!!!!!!" << std::endl;
882  return (false);
883  }
884 
885  // if (denom != 0 )
886  if (std::fabs(denom) > std::numeric_limits<double>::epsilon()) {
887  i = ((b2 / b1) * c1 - c2) / denom;
888  j = (-a1 * i - c1) / b1;
889  }
890  }
891  ip.set_i(i);
892  ip.set_j(j);
893 
894  return (true);
895  }
896  catch (...) {
897  return (false);
898  }
899 }
900 
901 #ifdef VISP_BUILD_DEPRECATED_FUNCTIONS
923 void vpMeLine::display(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
924  const double &B, const double &C, const vpColor &color, unsigned int thickness)
925 {
926  vpMeLine::displayLine(I, PExt1, PExt2, A, B, C, color, thickness);
927 }
928 
950 void vpMeLine::display(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
951  const double &B, const double &C, const vpColor &color, unsigned int thickness)
952 {
953  vpMeLine::displayLine(I, PExt1, PExt2, A, B, C, color, thickness);
954 }
955 
979 void vpMeLine::display(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
980  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
981  const vpColor &color, unsigned int thickness)
982 {
983  vpMeLine::displayLine(I, PExt1, PExt2, site_list, A, B, C, color, thickness);
984 }
985 
1009 void vpMeLine::display(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
1010  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
1011  const vpColor &color, unsigned int thickness)
1012 {
1013 
1014  vpMeLine::displayLine(I, PExt1, PExt2, site_list, A, B, C, color, thickness);
1015 }
1016 #endif // Deprecated
1017 
1018 void vpMeLine::displayLine(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
1019  const double &B, const double &C, const vpColor &color, unsigned int thickness)
1020 {
1021  vpImagePoint ip1, ip2;
1022 
1023  if (fabs(A) < fabs(B)) {
1024  double i1, j1, i2, j2;
1025  i1 = 0;
1026  j1 = (-A * i1 - C) / B;
1027  i2 = I.getHeight() - 1.0;
1028  j2 = (-A * i2 - C) / B;
1029 
1030  ip1.set_i(i1);
1031  ip1.set_j(j1);
1032  ip2.set_i(i2);
1033  ip2.set_j(j2);
1034  vpDisplay::displayLine(I, ip1, ip2, color);
1035  }
1036  else {
1037  double i1, j1, i2, j2;
1038  j1 = 0;
1039  i1 = -(B * j1 + C) / A;
1040  j2 = I.getWidth() - 1.0;
1041  i2 = -(B * j2 + C) / A;
1042 
1043  ip1.set_i(i1);
1044  ip1.set_j(j1);
1045  ip2.set_i(i2);
1046  ip2.set_j(j2);
1047  vpDisplay::displayLine(I, ip1, ip2, color);
1048  }
1049 
1050  ip1.set_i(PExt1.m_ifloat);
1051  ip1.set_j(PExt1.m_jfloat);
1052  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1053 
1054  ip1.set_i(PExt2.m_ifloat);
1055  ip1.set_j(PExt2.m_jfloat);
1056  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1057 }
1058 
1059 void vpMeLine::displayLine(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2, const double &A,
1060  const double &B, const double &C, const vpColor &color, unsigned int thickness)
1061 {
1062  vpImagePoint ip1, ip2;
1063 
1064  if (fabs(A) < fabs(B)) {
1065  double i1, j1, i2, j2;
1066  i1 = 0;
1067  j1 = (-A * i1 - C) / B;
1068  i2 = I.getHeight() - 1.0;
1069  j2 = (-A * i2 - C) / B;
1070 
1071  ip1.set_i(i1);
1072  ip1.set_j(j1);
1073  ip2.set_i(i2);
1074  ip2.set_j(j2);
1075  vpDisplay::displayLine(I, ip1, ip2, color);
1076  }
1077  else {
1078  double i1, j1, i2, j2;
1079  j1 = 0;
1080  i1 = -(B * j1 + C) / A;
1081  j2 = I.getWidth() - 1.0;
1082  i2 = -(B * j2 + C) / A;
1083 
1084  ip1.set_i(i1);
1085  ip1.set_j(j1);
1086  ip2.set_i(i2);
1087  ip2.set_j(j2);
1088  vpDisplay::displayLine(I, ip1, ip2, color);
1089  }
1090 
1091  ip1.set_i(PExt1.m_ifloat);
1092  ip1.set_j(PExt1.m_jfloat);
1093  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1094 
1095  ip1.set_i(PExt2.m_ifloat);
1096  ip1.set_j(PExt2.m_jfloat);
1097  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1098 }
1099 
1100 void vpMeLine::displayLine(const vpImage<unsigned char> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
1101  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
1102  const vpColor &color, unsigned int thickness)
1103 {
1104  vpImagePoint ip;
1105 
1106  for (std::list<vpMeSite>::const_iterator it = site_list.begin(); it != site_list.end(); ++it) {
1107  vpMeSite pix = *it;
1108  ip.set_i(pix.m_ifloat);
1109  ip.set_j(pix.m_jfloat);
1110 
1111  if (pix.getState() == vpMeSite::M_ESTIMATOR)
1112  vpDisplay::displayCross(I, ip, 5, vpColor::green, thickness);
1113  else
1114  vpDisplay::displayCross(I, ip, 5, color, thickness);
1115  }
1116 
1117  vpImagePoint ip1, ip2;
1118 
1119  if (fabs(A) < fabs(B)) {
1120  double i1, j1, i2, j2;
1121  i1 = 0;
1122  j1 = (-A * i1 - C) / B;
1123  i2 = I.getHeight() - 1.0;
1124  j2 = (-A * i2 - C) / B;
1125 
1126  ip1.set_i(i1);
1127  ip1.set_j(j1);
1128  ip2.set_i(i2);
1129  ip2.set_j(j2);
1130  vpDisplay::displayLine(I, ip1, ip2, color);
1131  }
1132  else {
1133  double i1, j1, i2, j2;
1134  j1 = 0;
1135  i1 = -(B * j1 + C) / A;
1136  j2 = I.getWidth() - 1.0;
1137  i2 = -(B * j2 + C) / A;
1138 
1139  ip1.set_i(i1);
1140  ip1.set_j(j1);
1141  ip2.set_i(i2);
1142  ip2.set_j(j2);
1143  vpDisplay::displayLine(I, ip1, ip2, color);
1144  }
1145 
1146  ip1.set_i(PExt1.m_ifloat);
1147  ip1.set_j(PExt1.m_jfloat);
1148  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1149 
1150  ip1.set_i(PExt2.m_ifloat);
1151  ip1.set_j(PExt2.m_jfloat);
1152  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1153 }
1154 
1155 void vpMeLine::displayLine(const vpImage<vpRGBa> &I, const vpMeSite &PExt1, const vpMeSite &PExt2,
1156  const std::list<vpMeSite> &site_list, const double &A, const double &B, const double &C,
1157  const vpColor &color, unsigned int thickness)
1158 {
1159  vpImagePoint ip;
1160 
1161  for (std::list<vpMeSite>::const_iterator it = site_list.begin(); it != site_list.end(); ++it) {
1162  vpMeSite pix = *it;
1163  ip.set_i(pix.m_ifloat);
1164  ip.set_j(pix.m_jfloat);
1165 
1166  if (pix.getState() == vpMeSite::M_ESTIMATOR)
1167  vpDisplay::displayCross(I, ip, 5, vpColor::green, thickness);
1168  else
1169  vpDisplay::displayCross(I, ip, 5, color, thickness);
1170  }
1171 
1172  vpImagePoint ip1, ip2;
1173 
1174  if (fabs(A) < fabs(B)) {
1175  double i1, j1, i2, j2;
1176  i1 = 0;
1177  j1 = (-A * i1 - C) / B;
1178  i2 = I.getHeight() - 1.0;
1179  j2 = (-A * i2 - C) / B;
1180 
1181  ip1.set_i(i1);
1182  ip1.set_j(j1);
1183  ip2.set_i(i2);
1184  ip2.set_j(j2);
1185  vpDisplay::displayLine(I, ip1, ip2, color);
1186  }
1187  else {
1188  double i1, j1, i2, j2;
1189  j1 = 0;
1190  i1 = -(B * j1 + C) / A;
1191  j2 = I.getWidth() - 1.0;
1192  i2 = -(B * j2 + C) / A;
1193 
1194  ip1.set_i(i1);
1195  ip1.set_j(j1);
1196  ip2.set_i(i2);
1197  ip2.set_j(j2);
1198  vpDisplay::displayLine(I, ip1, ip2, color);
1199  }
1200 
1201  ip1.set_i(PExt1.m_ifloat);
1202  ip1.set_j(PExt1.m_jfloat);
1203  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1204 
1205  ip1.set_i(PExt2.m_ifloat);
1206  ip1.set_j(PExt2.m_jfloat);
1207  vpDisplay::displayCross(I, ip1, 10, vpColor::green, thickness);
1208 }
1209 END_VISP_NAMESPACE
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:217
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 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
@ 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_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:409
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
vpMatrix pseudoInverse(double svThreshold=1e-6) const
Class that tracks in an image a line moving edges.
Definition: vpMeLine.h:152
double m_angle
Angle in deg between the extremities.
Definition: vpMeLine.h:163
double m_angle_1
Angle in deg between the extremities.
Definition: vpMeLine.h:164
double m_theta
theta parameter of the line
Definition: vpMeLine.h:160
int m_sign
Sign.
Definition: vpMeLine.h:165
void suppressPoints()
Definition: vpMeLine.cpp:426
double m_delta_1
Angle in rad between the extremities.
Definition: vpMeLine.h:162
double getRho() const
Definition: vpMeLine.cpp:834
void computeRhoTheta(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:731
void updateDelta()
Definition: vpMeLine.cpp:629
void display(const vpImage< unsigned char > &I, const vpColor &color, unsigned int thickness=1)
Definition: vpMeLine.cpp:194
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:173
double getTheta() const
Definition: vpMeLine.cpp:836
bool m_useIntensityForRho
Definition: vpMeLine.h:169
double m_delta
Angle in rad between the extremities.
Definition: vpMeLine.h:161
double m_rho
rho parameter of the line
Definition: vpMeLine.h:159
void seekExtremities(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:485
vpMeLine()
Definition: vpMeLine.cpp:91
void track(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:664
static bool intersection(const vpMeLine &line1, const vpMeLine &line2, vpImagePoint &ip)
Definition: vpMeLine.cpp:847
void getExtremities(vpImagePoint &ip1, vpImagePoint &ip2)
Definition: vpMeLine.cpp:838
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:1018
double m_a
Parameter a of the line equation a*i + b*j + c = 0.
Definition: vpMeLine.h:171
virtual void sample(const vpImage< unsigned char > &I, bool doNotTrack=false) VP_OVERRIDE
Definition: vpMeLine.cpp:122
double m_b
Parameter b of the line equation a*i + b*j + c = 0.
Definition: vpMeLine.h:172
vpMeSite m_PExt[2]
Line extremities.
Definition: vpMeLine.h:157
void leastSquare()
Definition: vpMeLine.cpp:225
void setExtremities()
Definition: vpMeLine.cpp:439
void initTracking(const vpImage< unsigned char > &I)
Definition: vpMeLine.cpp:199
virtual ~vpMeLine() VP_OVERRIDE
Definition: vpMeLine.cpp:117
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:250
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:214
void setAlpha(const double &a)
Definition: vpMeSite.h:245
void init()
Definition: vpMeSite.cpp:57
vpMeSiteState getState() const
Definition: vpMeSite.h:269
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:289
void track(const vpImage< unsigned char > &I, const vpMe *me, const bool &test_contrast=true)
Definition: vpMeSite.cpp:269
void setState(const vpMeSiteState &flag)
Definition: vpMeSite.h:259
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.
#define vpCDEBUG(level)
Definition: vpDebug.h:540
#define vpDERROR_TRACE
Definition: vpDebug.h:485
#define vpERROR_TRACE
Definition: vpDebug.h:409
#define vpDEBUG_ENABLE(level)
Definition: vpDebug.h:571