Visual Servoing Platform  version 3.6.1 under development (2024-04-19)
vpMe.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 <stdlib.h>
40 #include <visp3/core/vpColVector.h>
41 #include <visp3/core/vpMath.h>
42 #include <visp3/me/vpMe.h>
43 
44 #ifndef DOXYGEN_SHOULD_SKIP_THIS
45 namespace
46 {
47 struct vpPoint2Dt
48 {
49  double x;
50  double y;
51 };
52 
53 struct vpDroite2Dt
54 {
55  double a;
56  double b;
57  double c;
58 };
59 
60 template <class Type> inline void permute(Type &a, Type &b)
61 {
62  Type t = a;
63  a = b;
64  b = t;
65 }
66 
67 static vpDroite2Dt droiteCartesienne(vpPoint2Dt P, vpPoint2Dt Q)
68 {
69  vpDroite2Dt PQ;
70 
71  PQ.a = P.y - Q.y;
72  PQ.b = Q.x - P.x;
73  PQ.c = (Q.y * P.x) - (Q.x * P.y);
74 
75  return PQ;
76 }
77 
78 static vpPoint2Dt pointIntersection(vpDroite2Dt D1, vpDroite2Dt D2)
79 {
80  vpPoint2Dt I;
81  double det; // determinant des 2 vect.normaux
82 
83  det = ((D1.a * D2.b) - (D2.a * D1.b)); // interdit D1,D2 paralleles
84  I.x = ((D2.c * D1.b) - (D1.c * D2.b)) / det;
85  I.y = ((D1.c * D2.a) - (D2.c * D1.a)) / det;
86 
87  return I;
88 }
89 
90 static void recale(vpPoint2Dt &P, double Xmin, double Ymin, double Xmax, double Ymax)
91 {
92  if (vpMath::equal(P.x, Xmin)) {
93  P.x = Xmin; // a peu pres => exactement !
94  }
95 
96  if (vpMath::equal(P.x, Xmax)) {
97  P.x = Xmax;
98  }
99 
100  if (vpMath::equal(P.y, Ymin)) {
101  P.y = Ymin;
102  }
103 
104  if (vpMath::equal(P.y, Ymax)) {
105  P.y = Ymax;
106  }
107 }
108 
109 static void permute(vpPoint2Dt &A, vpPoint2Dt &B)
110 {
111  vpPoint2Dt C;
112 
113  if (A.x > B.x) // fonction sans doute a tester...
114  {
115  C = A;
116  A = B;
117  B = C;
118  }
119 }
120 
121 // vrai si partie visible
122 static bool clipping(vpPoint2Dt A, vpPoint2Dt B, double Xmin, double Ymin, double Xmax, double Ymax, vpPoint2Dt &Ac,
123  vpPoint2Dt &Bc) // resultat: A,B clippes
124 {
125  vpDroite2Dt AB, D[4];
126  D[0].a = 1;
127  D[0].b = 0;
128  D[0].c = -Xmin;
129  D[1].a = 1;
130  D[1].b = 0;
131  D[1].c = -Xmax;
132  D[2].a = 0;
133  D[2].b = 1;
134  D[2].c = -Ymin;
135  D[3].a = 0;
136  D[3].b = 1;
137  D[3].c = -Ymax;
138 
139  const int nbP = 2;
140  vpPoint2Dt P[nbP];
141  P[0] = A;
142  P[1] = B;
143  unsigned int code_P[nbP], // codes de P[n]
144  i, bit_i, // i -> (0000100...)
145  n;
146 
147  AB = droiteCartesienne(A, B);
148 
149  for (;;) // 2 sorties directes internes
150  {
151  // CALCULE CODE DE VISIBILITE (Sutherland & Sproul)
152  // ================================================
153  for (n = 0; n < nbP; ++n) {
154  code_P[n] = 0;
155 
156  if (P[n].x < Xmin) {
157  code_P[n] |= 1; // positionne bit0
158  }
159 
160  if (P[n].x > Xmax) {
161  code_P[n] |= 2; // .. bit1
162  }
163 
164  if (P[n].y < Ymin) {
165  code_P[n] |= 4; // .. bit2
166  }
167 
168  if (P[n].y > Ymax) {
169  code_P[n] |= 8; // .. bit3
170  }
171  }
172 
173  // 2 CAS OU L'ON PEUT CONCLURE => sortie
174  // =====================================
175  if ((code_P[0] | code_P[1]) == 0) {
176  Ac = P[0];
177  Bc = P[1];
178  if (vpMath::equal(Ac.x, Bc.x) && vpMath::equal(Ac.y, Bc.y)) {
179  return false; // AB = 1 point = invisible
180  }
181  else {
182  return true; // Partie de AB clippee visible!
183  }
184  }
185 
186  if ((code_P[0] & code_P[1]) != 0) // au moins 1 bit commun
187  {
188  return false; // AB completement invisible!
189  }
190 
191  // CAS GENERAL (on sait que code_P[0 ou 1] a au moins un bit a 1
192  // - clippe le point P[n] qui sort de la fenetre (coupe Droite i)
193  // - reboucle avec le nouveau couple de points
194  // ================================================================
195  if (code_P[0] != 0) {
196  n = 0; // c'est P[0] qu'on clippera
197  bit_i = 1;
198  for (i = 0; !(code_P[0] & bit_i); ++i) {
199  bit_i <<= 1;
200  }
201  }
202  else {
203  n = 1; // c'est P[1] qu'on clippera
204  bit_i = 1;
205  for (i = 0; !(code_P[1] & bit_i); ++i) {
206  bit_i <<= 1;
207  }
208  }
209 
210  P[n] = pointIntersection(AB, D[i]); // clippe le point concerne
211 
212  // RECALE EXACTEMENT LE POINT (calcul flottant => arrondi)
213  // AFIN QUE LE CALCUL DES CODES NE BOUCLE PAS INDEFINIMENT
214  // =======================================================
215  recale(P[n], Xmin, Ymin, Xmax, Ymax);
216  }
217 }
218 
219 // calcule la surface relative des 2 portions definies
220 // par le segment PQ sur le carre Xmin,Ymin,Xmax,Ymax
221 // Rem : P,Q tries sur x, et donc seulement 6 cas
222 static double surfaceRelative(vpPoint2Dt P, vpPoint2Dt Q, double Xmin, double Ymin, double Xmax, double Ymax)
223 {
224 
225  if (Q.x < P.x) { // tri le couple de points
226  permute(P, Q); // selon leur abscisse x
227  }
228 
229  recale(P, Xmin, Ymin, Xmax, Ymax); // permet des calculs de S_relative
230  recale(Q, Xmin, Ymin, Xmax, Ymax); // moins approximatifs.
231 
232  // Case P.x=Xmin and Q.x=Xmax
233  if ((std::fabs(P.x - Xmin) <=
234  (vpMath::maximum(std::fabs(P.x), std::fabs(Xmin)) * std::numeric_limits<double>::epsilon())) &&
235  (std::fabs(Q.x - Xmax) <=
236  (vpMath::maximum(std::fabs(Q.x), std::fabs(Xmax)) * std::numeric_limits<double>::epsilon()))) {
237  return fabs((Ymax + Ymin) - (P.y - Q.y));
238  }
239 
240  // Case (P.y=Ymin and Q.y==Ymax) or (Q.y=Ymin and P.y==Ymax)
241  if (((std::fabs(P.y - Ymin) <=
242  (vpMath::maximum(std::fabs(P.y), std::fabs(Ymin)) * std::numeric_limits<double>::epsilon())) &&
243  (std::fabs(Q.y - Ymax) <=
244  (vpMath::maximum(std::fabs(Q.y), std::fabs(Ymax)) * std::numeric_limits<double>::epsilon()))) ||
245  ((std::fabs(Q.y - Ymin) <=
246  (vpMath::maximum(std::fabs(Q.y), std::fabs(Ymin)) * std::numeric_limits<double>::epsilon())) &&
247  (std::fabs(P.y - Ymax) <=
248  (vpMath::maximum(std::fabs(P.y), std::fabs(Ymax)) * std::numeric_limits<double>::epsilon())))) {
249  return fabs((Xmax + Xmin) - (P.x - Q.x));
250  }
251 
252  // Case P.x=Xmin and Q.y=Ymax
253  if ((std::fabs(P.x - Xmin) <=
254  (vpMath::maximum(std::fabs(P.x), std::fabs(Xmin)) * std::numeric_limits<double>::epsilon())) &&
255  (std::fabs(Q.y - Ymax) <=
256  (vpMath::maximum(std::fabs(Q.y), std::fabs(Ymax)) * std::numeric_limits<double>::epsilon()))) {
257  return (1 - ((Ymax - P.y) * (Q.x - Xmin)));
258  }
259 
260  // Case P.x=Xmin and Q.y=Ymin
261  if ((std::fabs(P.x - Xmin) <=
262  (vpMath::maximum(std::fabs(P.x), std::fabs(Xmin)) * std::numeric_limits<double>::epsilon())) &&
263  (std::fabs(Q.y - Ymin) <=
264  (vpMath::maximum(std::fabs(Q.y), std::fabs(Ymin)) * std::numeric_limits<double>::epsilon()))) {
265  return (1 - ((P.y - Ymin) * (Q.x - Xmin)));
266  }
267 
268  // Case P.y=Ymin and Q.x=Xmax
269  if ((std::fabs(P.y - Ymin) <=
270  (vpMath::maximum(std::fabs(P.y), std::fabs(Ymin)) * std::numeric_limits<double>::epsilon())) &&
271  (std::fabs(Q.x - Xmax) <=
272  (vpMath::maximum(std::fabs(Q.x), std::fabs(Xmax)) * std::numeric_limits<double>::epsilon()))) {
273  return (1 - ((Xmax - P.x) * (Q.y - Ymin)));
274  }
275 
276  // Case P.y=Ymax and Q.x=Xmax
277  if ((std::fabs(P.y - Ymax) <=
278  (vpMath::maximum(std::fabs(P.y), std::fabs(Ymax)) * std::numeric_limits<double>::epsilon())) &&
279  (std::fabs(Q.x - Xmax) <=
280  (vpMath::maximum(std::fabs(Q.x), std::fabs(Xmax)) * std::numeric_limits<double>::epsilon()))) {
281  return (1 - ((Xmax - P.x) * (Ymax - Q.y)));
282  }
283 
284  throw(vpException(vpException::fatalError, "utils_ecm: error in surfaceRelative (%f,%f) (%f,%f) %f %f %f %f", P.x, P.y, Q.x, Q.y, Xmin, Ymin, Xmax, Ymax));
285 }
286 
287 static void calculMasques(vpColVector &angle, // definitions des angles theta
288  unsigned int n, // taille masques (PAIRE ou IMPAIRE Ok)
289  vpMatrix *M) // resultat M[theta](n,n)
290 {
291  unsigned int i, j;
292  double X, Y, moitie = (static_cast<double>(n)) / 2.0; // moitie REELLE du masque
293  vpPoint2Dt P1, Q1, P, Q; // clippe Droite(theta) P1,Q1 -> P,Q
294  double v; // ponderation de M(i,j)
295 
296  // For a mask of size nxn, normalization given by n*trunc(n/2.0)
297  // Typically, norm = 1/10 for a mask of size 5x5
298  double norm = 1.0 / (n * trunc(n / 2.0));
299 
300  unsigned int nb_theta = angle.getRows();
301 
302  for (unsigned int i_theta = 0; i_theta < nb_theta; ++i_theta) {
303  double theta = (M_PI / 180) * angle[i_theta]; // indice i -> theta(i) en radians
304  // angle[] dans [0,180[
305  double cos_theta = cos(theta); // vecteur directeur de l'ECM
306  double sin_theta = sin(theta); // associe au masque
307 
308  // PRE-CALCULE 2 POINTS DE D(theta) BIEN EN DEHORS DU MASQUE
309  // =========================================================
310  // if( angle[i_theta]==90 ) // => tan(theta) infinie !
311  const double thetaWhoseTanInfinite = 90.;
312  if (std::fabs(angle[i_theta] - thetaWhoseTanInfinite) <= (vpMath::maximum(std::fabs(angle[i_theta]), thetaWhoseTanInfinite) *
313  std::numeric_limits<double>::epsilon())) // => tan(theta) infinie !
314  {
315  P1.x = 0;
316  P1.y = -static_cast<int>(n);
317  Q1.x = 0;
318  Q1.y = n;
319  }
320  else {
321  double tan_theta = sin_theta / cos_theta; // pente de la droite D(theta)
322  P1.x = -static_cast<int>(n);
323  P1.y = tan_theta * (-static_cast<int>(n));
324  Q1.x = n;
325  Q1.y = tan_theta * n;
326  }
327 
328  // CALCULE MASQUE M(theta)
329  // ======================
330  M[i_theta].resize(n, n); // allocation (si necessaire)
331 
332  for (i = 0, Y = (-moitie + 0.5); i < n; ++i, ++Y) {
333  for (j = 0, X = (-moitie + 0.5); j < n; ++j, ++X) {
334  // produit vectoriel dir_droite*(X,Y)
335  int sgn = vpMath::sign((cos_theta * Y) - (sin_theta * X));
336 
337  // Resultat = P,Q
338  if (clipping(P1, Q1, X - 0.5, Y - 0.5, X + 0.5, Y + 0.5, P, Q)) {
339  // v dans [0,1]
340  v = surfaceRelative(P, Q, X - 0.5, Y - 0.5, X + 0.5, Y + 0.5);
341  }
342  else {
343  v = 1; // PQ ne coupe pas le pixel(i,j)
344  }
345 
346  M[i_theta][i][j] = sgn * v * norm;
347  }
348  }
349  }
350 }
351 }
352 #endif
353 
355 {
356  if (m_mask != nullptr) {
357  delete[] m_mask;
358  }
359 
360  m_mask = new vpMatrix[m_mask_number];
361 
362  vpColVector angle(m_mask_number);
363 
364  unsigned int angle_pas = 180 / m_mask_number;
365 
366  unsigned int i = 0;
367  for (unsigned int k = 0; k < m_mask_number; ++k) {
368  angle[k] = i;
369  i += angle_pas;
370  }
371 
372  calculMasques(angle, m_mask_size, m_mask);
373 }
374 
376 {
377  std::cout << std::endl;
378  std::cout << "Moving edges settings " << std::endl;
379  std::cout << std::endl;
380  std::cout << " Size of the convolution masks...." << m_mask_size << "x" << m_mask_size << " pixels" << std::endl;
381  std::cout << " Number of masks.................." << m_mask_number << std::endl;
382  std::cout << " Query range +/- J................" << m_range << " pixels" << std::endl;
383  std::cout << " Likelihood threshold type........" << (m_likelihood_threshold_type == NORMALIZED_THRESHOLD ? "normalized " : "old threshold (to be avoided)") << std::endl;
384 
385  if (m_useAutomaticThreshold) {
386  std::cout << " Likelihood threshold............." << "unused" << std::endl;
387  std::cout << " Likelihood margin ratio.........." << m_thresholdMarginRatio << std::endl;
388  std::cout << " Minimum likelihood threshold....." << m_minThreshold << std::endl;
389  }
390  else {
391  std::cout << " Likelihood threshold............." << m_threshold << std::endl;
392  std::cout << " Likelihood margin ratio.........." << "unused" << std::endl;
393  std::cout << " Minimum likelihood threshold....." << "unused" << std::endl;
394  }
395 
396  std::cout << " Contrast tolerance +/-..........." << m_mu1 * 100 << "% and " << m_mu2 * 100 << "% " << std::endl;
397  std::cout << " Sample step......................" << m_sample_step << " pixels" << std::endl;
398  std::cout << " Strip............................" << m_strip << " pixels " << std::endl;
399  std::cout << " Min sample step.................." << m_min_samplestep << " pixels " << std::endl;
400 }
401 
403  : m_likelihood_threshold_type(OLD_THRESHOLD), m_threshold(10000), m_thresholdMarginRatio(-1), m_minThreshold(-1), m_useAutomaticThreshold(false),
404  m_mu1(0.5), m_mu2(0.5), m_min_samplestep(4), m_anglestep(1), m_mask_sign(0), m_range(4), m_sample_step(10),
405  m_ntotal_sample(0), m_points_to_track(500), m_mask_size(5), m_mask_number(180), m_strip(2), m_mask(nullptr)
406 {
407  const unsigned int flatAngle = 180;
408  m_anglestep = (flatAngle / m_mask_number);
409 
410  initMask();
411 }
412 
413 vpMe::vpMe(const vpMe &me)
414  : m_likelihood_threshold_type(OLD_THRESHOLD), m_threshold(10000), m_thresholdMarginRatio(-1), m_minThreshold(-1), m_useAutomaticThreshold(false),
415  m_mu1(0.5), m_mu2(0.5), m_min_samplestep(4), m_anglestep(1), m_mask_sign(0), m_range(4), m_sample_step(10),
416  m_ntotal_sample(0), m_points_to_track(500), m_mask_size(5), m_mask_number(180), m_strip(2), m_mask(nullptr)
417 {
418  *this = me;
419 }
420 
422 {
423  if (m_mask != nullptr) {
424  delete[] m_mask;
425  m_mask = nullptr;
426  }
427 
428  m_likelihood_threshold_type = me.m_likelihood_threshold_type;
429  m_threshold = me.m_threshold;
430  m_thresholdMarginRatio = me.m_thresholdMarginRatio;
431  m_minThreshold = me.m_minThreshold;
432  m_useAutomaticThreshold = me.m_useAutomaticThreshold;
433  m_mu1 = me.m_mu1;
434  m_mu2 = me.m_mu2;
435  m_min_samplestep = me.m_min_samplestep;
436  m_anglestep = me.m_anglestep;
437  m_mask_size = me.m_mask_size;
438  m_mask_number = me.m_mask_number;
439  m_mask_sign = me.m_mask_sign;
440  m_range = me.m_range;
441  m_sample_step = me.m_sample_step;
442  m_ntotal_sample = me.m_ntotal_sample;
443  m_points_to_track = me.m_points_to_track;
444  m_strip = me.m_strip;
445 
446  initMask();
447  return *this;
448 }
449 
450 #if (VISP_CXX_STANDARD >= VISP_CXX_STANDARD_11)
452 {
453  if (m_mask != nullptr) {
454  delete[] m_mask;
455  m_mask = nullptr;
456  }
457  m_likelihood_threshold_type = std::move(me.m_likelihood_threshold_type);
458  m_threshold = std::move(me.m_threshold);
459  m_thresholdMarginRatio = std::move(me.m_thresholdMarginRatio);
460  m_minThreshold = std::move(me.m_minThreshold);
461  m_useAutomaticThreshold = std::move(me.m_useAutomaticThreshold);
462  m_mu1 = std::move(me.m_mu1);
463  m_mu2 = std::move(me.m_mu2);
464  m_min_samplestep = std::move(me.m_min_samplestep);
465  m_anglestep = std::move(me.m_anglestep);
466  m_mask_size = std::move(me.m_mask_size);
467  m_mask_number = std::move(me.m_mask_number);
468  m_mask_sign = std::move(me.m_mask_sign);
469  m_range = std::move(me.m_range);
470  m_sample_step = std::move(me.m_sample_step);
471  m_ntotal_sample = std::move(me.m_ntotal_sample);
472  m_points_to_track = std::move(me.m_points_to_track);
473  m_strip = std::move(me.m_strip);
474 
475  initMask();
476  return *this;
477 }
478 #endif
479 
481 {
482  if (m_mask != nullptr) {
483  delete[] m_mask;
484  m_mask = nullptr;
485  }
486 }
487 
488 void vpMe::setMaskNumber(const unsigned int &mask_number)
489 {
490  const unsigned int flatAngle = 180;
491  m_mask_number = mask_number;
492  m_anglestep = flatAngle / m_mask_number;
493  initMask();
494 }
495 
496 void vpMe::setMaskSize(const unsigned int &mask_size)
497 {
498  m_mask_size = mask_size;
499  initMask();
500 }
void resize(unsigned int nrows, unsigned int ncols, bool flagNullify=true, bool recopy_=true)
Definition: vpArray2D.h:352
unsigned int getRows() const
Definition: vpArray2D.h:337
Implementation of column vector and the associated operations.
Definition: vpColVector.h:163
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ fatalError
Fatal error.
Definition: vpException.h:84
static Type maximum(const Type &a, const Type &b)
Definition: vpMath.h:252
static bool equal(double x, double y, double threshold=0.001)
Definition: vpMath.h:449
static int sign(double x)
Definition: vpMath.h:422
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:146
Definition: vpMe.h:124
void print()
Definition: vpMe.cpp:375
void setMaskNumber(const unsigned int &mask_number)
Definition: vpMe.cpp:488
virtual ~vpMe()
Definition: vpMe.cpp:480
vpMe()
Definition: vpMe.cpp:402
void setMaskSize(const unsigned int &mask_size)
Definition: vpMe.cpp:496
void initMask()
Definition: vpMe.cpp:354
vpMe & operator=(const vpMe &me)
Definition: vpMe.cpp:421
@ NORMALIZED_THRESHOLD
Definition: vpMe.h:135