Visual Servoing Platform  version 3.6.1 under development (2025-01-20)
vpMath.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 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  * Simple mathematical function not available in the C math library (math.h).
32  */
33 
40 #include <cmath>
41 #include <functional>
42 #include <numeric>
43 #include <stdint.h>
44 #include <cassert>
45 #include <ctype.h>
46 
47 #include <visp3/core/vpException.h>
48 #include <visp3/core/vpMath.h>
49 #include <visp3/core/vpMatrix.h>
50 
51 #if defined(VISP_HAVE_FUNC__FINITE)
52 #include <float.h>
53 #endif
54 
55 BEGIN_VISP_NAMESPACE
56 #if !(defined(VISP_HAVE_FUNC_ISNAN) || defined(VISP_HAVE_FUNC_STD_ISNAN)) || \
57  !(defined(VISP_HAVE_FUNC_ISINF) || defined(VISP_HAVE_FUNC_STD_ISINF)) || \
58  !(defined(VISP_HAVE_FUNC_ISFINITE) || defined(VISP_HAVE_FUNC_STD_ISFINITE) || defined(VISP_HAVE_FUNC__FINITE))
59 #if defined _MSC_VER || defined __BORLANDC__
60 typedef __int64 int64;
61 typedef unsigned __int64 uint64;
62 #else
63 typedef int64_t int64;
64 typedef uint64_t uint64;
65 #endif
66 
67 #ifndef DOXYGEN_SHOULD_SKIP_THIS
68 typedef union Vp64suf
69 {
70  // int64 i; //Unused variable, should be harmless to comment it
71  uint64 u;
72  double f;
73 } Vp64suf;
74 
75 typedef union Vp32suf
76 {
77  // int i; //Unused variable, should be harmless to comment it
78  unsigned u;
79  float f;
80 } Vp32suf;
81 #endif
82 #endif
83 
84 const double vpMath::ang_min_sinc = 1.0e-8;
85 const double vpMath::ang_min_mc = 2.5e-4;
86 
92 bool vpMath::isNaN(double value)
93 {
94 #if defined(VISP_HAVE_FUNC_STD_ISNAN)
95  return std::isnan(value);
96 #elif defined(VISP_HAVE_FUNC_ISNAN)
97  return isnan(value);
98 #elif defined(VISP_HAVE_FUNC__ISNAN)
99  return (_isnan(value) != 0);
100 #else
101  // Taken from OpenCV source code CvIsNan()
102  Vp64suf ieee754;
103  ieee754.f = value;
104  return (((unsigned)(ieee754.u >> 32) & 0x7fffffff) + ((unsigned)ieee754.u != 0) > 0x7ff00000) != 0;
105 #endif
106 }
107 
113 bool vpMath::isNaN(float value)
114 {
115 #if defined(VISP_HAVE_FUNC_STD_ISNAN)
116  return std::isnan(value);
117 #elif defined(VISP_HAVE_FUNC_ISNAN)
118  return isnan(value);
119 #elif defined(VISP_HAVE_FUNC__ISNAN)
120  return (_isnan(value) != 0);
121 #else
122  // Taken from OpenCV source code CvIsNan()
123  Vp32suf ieee754;
124  ieee754.f = value;
125  return ((unsigned)ieee754.u & 0x7fffffff) > 0x7f800000;
126 #endif
127 }
128 
136 bool vpMath::isInf(double value)
137 {
138 #if defined(VISP_HAVE_FUNC_STD_ISINF)
139  return std::isinf(value);
140 #elif defined(VISP_HAVE_FUNC_ISINF)
141  return isinf(value);
142 #else
143  // Taken from OpenCV source code CvIsInf()
144  Vp64suf ieee754;
145  ieee754.f = value;
146  return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && (unsigned)ieee754.u == 0;
147 #endif
148 }
149 
157 bool vpMath::isInf(float value)
158 {
159 #if defined(VISP_HAVE_FUNC_STD_ISINF)
160  return std::isinf(value);
161 #elif defined(VISP_HAVE_FUNC_ISINF)
162  return isinf(value);
163 #else
164  // Taken from OpenCV source code CvIsInf()
165  Vp32suf ieee754;
166  ieee754.f = value;
167  return ((unsigned)ieee754.u & 0x7fffffff) == 0x7f800000;
168 #endif
169 }
170 
177 bool vpMath::isFinite(double value)
178 {
179 #if defined(VISP_HAVE_FUNC_STD_ISFINITE)
180  return std::isfinite(value);
181 #elif defined(VISP_HAVE_FUNC_ISFINITE)
182  return isfinite(value);
183 #elif defined(VISP_HAVE_FUNC__FINITE)
184  return _finite(value);
185 #else
186  return !vpMath::isInf(value) && !vpMath::isNaN(value);
187 #endif
188 }
189 
196 bool vpMath::isFinite(float value)
197 {
198 #if defined(VISP_HAVE_FUNC_STD_ISFINITE)
199  return std::isfinite(value);
200 #elif defined(VISP_HAVE_FUNC_ISFINITE)
201  return isfinite(value);
202 #elif defined(VISP_HAVE_FUNC__FINITE)
203  return _finitef(value);
204 #else
205  return !vpMath::isInf(value) && !vpMath::isNaN(value);
206 #endif
207 }
208 
214 bool vpMath::isNumber(const std::string &str)
215 {
216  size_t str_size = str.size();
217  for (size_t i = 0; i < str_size; ++i) {
218  if (isdigit(str[i]) == false) {
219  return false;
220  }
221  }
222  return true;
223 }
224 
233 double vpMath::mcosc(double cosx, double x)
234 {
235  if (fabs(x) < ang_min_mc) {
236  return 0.5 - x * x / 24.0;
237  }
238  else {
239  return ((1.0 - cosx) / x / x);
240  }
241 }
242 
251 double vpMath::msinc(double sinx, double x)
252 {
253  if (fabs(x) < ang_min_mc) {
254  return (1. / 6.0 - x * x / 120.0);
255  }
256  else {
257  return ((1.0 - (sinx / x)) / x / x);
258  }
259 }
260 
268 double vpMath::sinc(double x)
269 {
270  if (fabs(x) < ang_min_sinc) {
271  return 1.0 - x * x / 6.0;
272  }
273  else {
274  return sin(x) / x;
275  }
276 }
285 double vpMath::sinc(double sinx, double x)
286 {
287  if (fabs(x) < ang_min_sinc) {
288  return 1.0 - x * x / 6.0;
289  }
290  else {
291  return (sinx / x);
292  }
293 }
294 
302 double vpMath::getMean(const std::vector<double> &v)
303 {
304  if (v.empty()) {
305  throw vpException(vpException::notInitialized, "Empty vector !");
306  }
307 
308  size_t size = v.size();
309 
310  double sum = std::accumulate(v.begin(), v.end(), 0.0);
311 
312  return sum / (static_cast<double>(size));
313 }
314 
322 double vpMath::getMedian(const std::vector<double> &v)
323 {
324  if (v.empty()) {
325  throw vpException(vpException::notInitialized, "Empty vector !");
326  }
327  const size_t val_2 = 2;
328  std::vector<double> v_copy = v;
329  size_t size = v_copy.size();
330 
331  size_t n = size / 2;
332  std::nth_element(v_copy.begin(), v_copy.begin() + n, v_copy.end());
333  double val_n = v_copy[n];
334 
335  if ((size % val_2) == 1) {
336  return val_n;
337  }
338  else {
339  std::nth_element(v_copy.begin(), v_copy.begin() + (n - 1), v_copy.end());
340  return 0.5 * (val_n + v_copy[n - 1]);
341  }
342 }
343 
353 double vpMath::getStdev(const std::vector<double> &v, bool useBesselCorrection)
354 {
355  if (v.empty()) {
356  throw vpException(vpException::notInitialized, "Empty vector !");
357  }
358 
359  double mean = getMean(v);
360 
361  std::vector<double> diff(v.size());
362 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
363  std::transform(v.begin(), v.end(), diff.begin(), std::bind(std::minus<double>(), std::placeholders::_1, mean));
364 #else
365  std::transform(v.begin(), v.end(), diff.begin(), std::bind2nd(std::minus<double>(), mean));
366 #endif
367 
368  double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
369  double divisor = static_cast<double> (v.size());
370  if (useBesselCorrection && (v.size() > 1)) {
371  divisor = divisor - 1;
372  }
373 
374  return std::sqrt(sq_sum / divisor);
375 }
376 
390 double vpMath::lineFitting(const std::vector<vpImagePoint> &imPts, double &a, double &b, double &c)
391 {
392  const unsigned int val_3 = 3;
393  if (imPts.size() < val_3) {
394  throw vpException(vpException::dimensionError, "Number of image points must be greater or equal to 3.");
395  }
396 
397  double x_mean = 0, y_mean = 0;
398  size_t imPts_size = imPts.size();
399  for (size_t i = 0; i < imPts_size; ++i) {
400  const vpImagePoint &imPt = imPts[i];
401  x_mean += imPt.get_u();
402  y_mean += imPt.get_v();
403  }
404  x_mean /= imPts.size();
405  y_mean /= imPts.size();
406 
407  vpMatrix AtA(2, 2, 0.0);
408  imPts_size = imPts.size();
409  for (size_t i = 0; i < imPts_size; ++i) {
410  const vpImagePoint &imPt = imPts[i];
411  AtA[0][0] += (imPt.get_u() - x_mean) * (imPt.get_u() - x_mean);
412  AtA[0][1] += (imPt.get_u() - x_mean) * (imPt.get_v() - y_mean);
413  AtA[1][1] += (imPt.get_v() - y_mean) * (imPt.get_v() - y_mean);
414  }
415  AtA[1][0] = AtA[0][1];
416 
417  vpColVector eigenvalues;
418  vpMatrix eigenvectors;
419  AtA.eigenValues(eigenvalues, eigenvectors);
420 
421  a = eigenvectors[0][0];
422  b = eigenvectors[1][0];
423  c = (a * x_mean) + (b * y_mean);
424 
425  double error = 0;
426  imPts_size = imPts.size();
427  for (size_t i = 0; i < imPts_size; ++i) {
428  double x0 = imPts[i].get_u();
429  double y0 = imPts[i].get_v();
430 
431  error += std::fabs((a * x0) + ((b * y0) - c));
432  }
433 
434  return error / imPts.size();
435 }
436 
447 int vpMath::modulo(int a, int n) { return ((a % n) + n) % n; }
448 
458 unsigned int vpMath::modulo(unsigned int a, unsigned int n) { return ((a % n) + n) % n; }
459 
498 vpHomogeneousMatrix vpMath::ned2ecef(double lonDeg, double latDeg, double radius)
499 {
500  double lon = vpMath::rad(lonDeg);
501  double lat = vpMath::rad(latDeg);
502  const unsigned int index_0 = 0;
503  const unsigned int index_1 = 1;
504  const unsigned int index_2 = 2;
505  const unsigned int index_3 = 3;
506  vpHomogeneousMatrix ecef_M_ned;
507  ecef_M_ned[index_0][index_0] = -sin(lat) * cos(lon);
508  ecef_M_ned[index_0][index_1] = -sin(lon);
509  ecef_M_ned[index_0][index_2] = -cos(lat) * cos(lon);
510  ecef_M_ned[index_0][index_3] = radius * cos(lon) * cos(lat);
511  ecef_M_ned[index_1][index_0] = -sin(lat) * sin(lon);
512  ecef_M_ned[index_1][index_1] = cos(lon);
513  ecef_M_ned[index_1][index_2] = -cos(lat) * sin(lon);
514  ecef_M_ned[index_1][index_3] = radius * sin(lon) * cos(lat);
515  ecef_M_ned[index_2][index_0] = cos(lat);
516  ecef_M_ned[index_2][index_1] = 0;
517  ecef_M_ned[index_2][index_2] = -sin(lat);
518  ecef_M_ned[index_2][index_3] = radius * sin(lat);
519 
520  return ecef_M_ned;
521 }
522 
562 vpHomogeneousMatrix vpMath::enu2ecef(double lonDeg, double latDeg, double radius)
563 {
564  double lon = vpMath::rad(lonDeg);
565  double lat = vpMath::rad(latDeg);
566  const unsigned int index_0 = 0;
567  const unsigned int index_1 = 1;
568  const unsigned int index_2 = 2;
569  const unsigned int index_3 = 3;
570 
571  vpHomogeneousMatrix ecef_M_enu;
572  ecef_M_enu[index_0][index_0] = -sin(lon);
573  ecef_M_enu[index_0][index_1] = -sin(lat) * cos(lon);
574  ecef_M_enu[index_0][index_2] = cos(lat) * cos(lon);
575  ecef_M_enu[index_0][index_3] = radius * cos(lon) * cos(lat);
576  ecef_M_enu[index_1][index_0] = cos(lon);
577  ecef_M_enu[index_1][index_1] = -sin(lat) * sin(lon);
578  ecef_M_enu[index_1][index_2] = cos(lat) * sin(lon);
579  ecef_M_enu[index_1][index_3] = radius * sin(lon) * cos(lat);
580  ecef_M_enu[index_2][index_0] = 0;
581  ecef_M_enu[index_2][index_1] = cos(lat);
582  ecef_M_enu[index_2][index_2] = sin(lat);
583  ecef_M_enu[index_2][index_3] = radius * sin(lat);
584 
585  return ecef_M_enu;
586 }
587 
602 std::vector<std::pair<double, double> > vpMath::computeRegularPointsOnSphere(unsigned int maxPoints)
603 {
604  assert(maxPoints > 0);
605 
606  double a = (4.0 * M_PI) / maxPoints;
607  double d = sqrt(a);
608  int m_theta = static_cast<int>(round(M_PI / d));
609  double d_theta = M_PI / m_theta;
610  double d_phi = a / d_theta;
611 
612  std::vector<std::pair<double, double> > lonlat_vec;
613 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
614  lonlat_vec.reserve(static_cast<unsigned int>(std::sqrt(maxPoints)));
615 #else
616  lonlat_vec.reserve(static_cast<unsigned int>(std::sqrt(static_cast<double>(maxPoints))));
617 #endif
618 
619  for (int m = 0; m < m_theta; ++m) {
620  double theta = (M_PI * (m + 0.5)) / m_theta;
621  int m_phi = static_cast<int>(round((2.0 * M_PI * sin(theta)) / d_phi));
622 
623  for (int n = 0; n < m_phi; ++n) {
624  double phi = (2.0 * M_PI * n) / m_phi;
625  double lon = phi;
626  double lat = M_PI_2 - theta;
627  lonlat_vec.push_back(std::make_pair(deg(lon), deg(lat)));
628  }
629  }
630 
631  return lonlat_vec;
632 }
633 
653 std::vector<vpHomogeneousMatrix> vpMath::getLocalTangentPlaneTransformations(const std::vector<std::pair<double, double> > &lonlatVec, double radius,
654  LongLattToHomogeneous toECEF)
655 {
656  std::vector<vpHomogeneousMatrix> ecef_M_local_vec;
657  ecef_M_local_vec.reserve(lonlatVec.size());
658  size_t lonlatVec_size = lonlatVec.size();
659  for (size_t i = 0; i < lonlatVec_size; ++i) {
660  double lonDeg = lonlatVec[i].first;
661  double latDeg = lonlatVec[i].second;
662 
663  vpHomogeneousMatrix ecef_M_local = toECEF(lonDeg, latDeg, radius);
664  ecef_M_local_vec.push_back(ecef_M_local);
665  }
666  return ecef_M_local_vec;
667 }
668 
690 {
691  assert(from.size() == 3);
692  assert(to.size() == 3);
693  assert(tmp.size() == 3);
694  vpColVector forward = (from - to).normalize();
695  vpColVector right = vpColVector::crossProd(tmp.normalize(), forward).normalize();
696  vpColVector up = vpColVector::crossProd(forward, right).normalize();
697  const unsigned int index_0 = 0;
698  const unsigned int index_1 = 1;
699  const unsigned int index_2 = 2;
700  const unsigned int index_3 = 3;
701 
703  wMc[index_0][index_0] = right[index_0];
704  wMc[index_0][index_1] = up[index_0];
705  wMc[index_0][index_2] = forward[index_0];
706  wMc[index_0][index_3] = from[index_0];
707  wMc[index_1][index_0] = right[index_1];
708  wMc[index_1][index_1] = up[index_1];
709  wMc[index_1][index_2] = forward[index_1];
710  wMc[index_1][index_3] = from[index_1];
711  wMc[index_2][index_0] = right[index_2];
712  wMc[index_2][index_1] = up[index_2];
713  wMc[index_2][index_2] = forward[index_2];
714  wMc[index_2][index_3] = from[index_2];
715 
716  return wMc;
717 }
718 
726 {
727  const unsigned int val_4 = 4;
728  if (r.size() == val_4) {
729  throw(vpException(vpException::fatalError, "Cannot convert angles of a quaternion vector in degrees!"));
730  }
731  vpColVector r_deg(r.size());
732  unsigned int r_size = r.size();
733  for (unsigned int i = 0; i < r_size; ++i) {
734  r_deg[i] = vpMath::deg(r[i]);
735  }
736  return r_deg;
737 }
738 
746 {
747  vpColVector r_deg(r.size());
748  unsigned int r_size = r.size();
749  for (unsigned int i = 0; i < r_size; ++i) {
750  r_deg[i] = vpMath::deg(r[i]);
751  }
752  return r_deg;
753 }
754 
762 {
763  vpColVector r_rad(r.size());
764  unsigned int r_size = r.size();
765  for (unsigned int i = 0; i < r_size; ++i) {
766  r_rad[i] = vpMath::rad(r[i]);
767  }
768  return r_rad;
769 }
770 
777 {
778  vpHomogeneousMatrix ned_M_enu;
779  const unsigned int index_0 = 0;
780  const unsigned int index_1 = 1;
781  const unsigned int index_2 = 2;
782  ned_M_enu[index_0][index_0] = 0;
783  ned_M_enu[index_0][index_1] = 1;
784  ned_M_enu[index_1][index_0] = 1;
785  ned_M_enu[index_1][index_1] = 0;
786  ned_M_enu[index_2][index_2] = -1;
787 
788  vpHomogeneousMatrix ned_M = ned_M_enu * enu_M;
789  return ned_M;
790 }
791 END_VISP_NAMESPACE
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:349
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
vpColVector & normalize()
static vpColVector crossProd(const vpColVector &a, const vpColVector &b)
error that can be emitted by ViSP classes.
Definition: vpException.h:60
@ notInitialized
Used to indicate that a parameter is not initialized.
Definition: vpException.h:74
@ dimensionError
Bad dimension.
Definition: vpException.h:71
@ fatalError
Fatal error.
Definition: vpException.h:72
Implementation of an homogeneous matrix and operations on such kind of matrices.
Class that defines a 2D point in an image. This class is useful for image processing and stores only ...
Definition: vpImagePoint.h:82
double get_u() const
Definition: vpImagePoint.h:136
double get_v() const
Definition: vpImagePoint.h:147
static double msinc(double sinx, double x)
Definition: vpMath.cpp:251
static bool isNaN(double value)
Definition: vpMath.cpp:92
static double rad(double deg)
Definition: vpMath.h:129
static double getMedian(const std::vector< double > &v)
Definition: vpMath.cpp:322
static double sinc(double x)
Definition: vpMath.cpp:268
static std::vector< vpHomogeneousMatrix > getLocalTangentPlaneTransformations(const std::vector< std::pair< double, double > > &lonlatVec, double radius, LongLattToHomogeneous func)
Definition: vpMath.cpp:653
static double getStdev(const std::vector< double > &v, bool useBesselCorrection=false)
Definition: vpMath.cpp:353
static vpHomogeneousMatrix lookAt(const vpColVector &from, const vpColVector &to, vpColVector tmp)
Definition: vpMath.cpp:689
static vpHomogeneousMatrix enu2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:562
static double lineFitting(const std::vector< vpImagePoint > &imPts, double &a, double &b, double &c)
Definition: vpMath.cpp:390
static int round(double x)
Definition: vpMath.h:410
static bool isFinite(double value)
Definition: vpMath.cpp:177
static double getMean(const std::vector< double > &v)
Definition: vpMath.cpp:302
static bool isInf(double value)
Definition: vpMath.cpp:136
static vpHomogeneousMatrix enu2ned(const vpHomogeneousMatrix &enu_M)
Definition: vpMath.cpp:776
static double mcosc(double cosx, double x)
Definition: vpMath.cpp:233
static double deg(double rad)
Definition: vpMath.h:119
static bool isNumber(const std::string &str)
Definition: vpMath.cpp:214
static float modulo(const float &value, const float &modulo)
Gives the rest of value divided by modulo when the quotient can only be an integer.
Definition: vpMath.h:177
static std::vector< std::pair< double, double > > computeRegularPointsOnSphere(unsigned int maxPoints)
Definition: vpMath.cpp:602
static vpHomogeneousMatrix ned2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:498
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:169
vpColVector eigenValues() const
Definition: vpMatrix.cpp:1192
Implementation of a generic rotation vector.