Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
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 
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 
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 % 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  if (imPts.size() < 3) {
393  throw vpException(vpException::dimensionError, "Number of image points must be greater or equal to 3.");
394  }
395 
396  double x_mean = 0, y_mean = 0;
397  size_t imPts_size = imPts.size();
398  for (size_t i = 0; i < imPts_size; ++i) {
399  const vpImagePoint &imPt = imPts[i];
400  x_mean += imPt.get_u();
401  y_mean += imPt.get_v();
402  }
403  x_mean /= imPts.size();
404  y_mean /= imPts.size();
405 
406  vpMatrix AtA(2, 2, 0.0);
407  imPts_size = imPts.size();
408  for (size_t i = 0; i < imPts_size; ++i) {
409  const vpImagePoint &imPt = imPts[i];
410  AtA[0][0] += (imPt.get_u() - x_mean) * (imPt.get_u() - x_mean);
411  AtA[0][1] += (imPt.get_u() - x_mean) * (imPt.get_v() - y_mean);
412  AtA[1][1] += (imPt.get_v() - y_mean) * (imPt.get_v() - y_mean);
413  }
414  AtA[1][0] = AtA[0][1];
415 
416  vpColVector eigenvalues;
417  vpMatrix eigenvectors;
418  AtA.eigenValues(eigenvalues, eigenvectors);
419 
420  a = eigenvectors[0][0];
421  b = eigenvectors[1][0];
422  c = (a * x_mean) + (b * y_mean);
423 
424  double error = 0;
425  imPts_size = imPts.size();
426  for (size_t i = 0; i < imPts_size; ++i) {
427  double x0 = imPts[i].get_u();
428  double y0 = imPts[i].get_v();
429 
430  error += std::fabs((a * x0) + ((b * y0) - c));
431  }
432 
433  return error / imPts.size();
434 }
435 
446 int vpMath::modulo(int a, int n) { return ((a % n) + n) % n; }
447 
486 vpHomogeneousMatrix vpMath::ned2ecef(double lonDeg, double latDeg, double radius)
487 {
488  double lon = vpMath::rad(lonDeg);
489  double lat = vpMath::rad(latDeg);
490  const unsigned int index_0 = 0;
491  const unsigned int index_1 = 1;
492  const unsigned int index_2 = 2;
493  const unsigned int index_3 = 3;
494  vpHomogeneousMatrix ecef_M_ned;
495  ecef_M_ned[index_0][index_0] = -sin(lat) * cos(lon);
496  ecef_M_ned[index_0][index_1] = -sin(lon);
497  ecef_M_ned[index_0][index_2] = -cos(lat) * cos(lon);
498  ecef_M_ned[index_0][index_3] = radius * cos(lon) * cos(lat);
499  ecef_M_ned[index_1][index_0] = -sin(lat) * sin(lon);
500  ecef_M_ned[index_1][index_1] = cos(lon);
501  ecef_M_ned[index_1][index_2] = -cos(lat) * sin(lon);
502  ecef_M_ned[index_1][index_3] = radius * sin(lon) * cos(lat);
503  ecef_M_ned[index_2][index_0] = cos(lat);
504  ecef_M_ned[index_2][index_1] = 0;
505  ecef_M_ned[index_2][index_2] = -sin(lat);
506  ecef_M_ned[index_2][index_3] = radius * sin(lat);
507 
508  return ecef_M_ned;
509 }
510 
550 vpHomogeneousMatrix vpMath::enu2ecef(double lonDeg, double latDeg, double radius)
551 {
552  double lon = vpMath::rad(lonDeg);
553  double lat = vpMath::rad(latDeg);
554  const unsigned int index_0 = 0;
555  const unsigned int index_1 = 1;
556  const unsigned int index_2 = 2;
557  const unsigned int index_3 = 3;
558 
559  vpHomogeneousMatrix ecef_M_enu;
560  ecef_M_enu[index_0][index_0] = -sin(lon);
561  ecef_M_enu[index_0][index_1] = -sin(lat) * cos(lon);
562  ecef_M_enu[index_0][index_2] = cos(lat) * cos(lon);
563  ecef_M_enu[index_0][index_3] = radius * cos(lon) * cos(lat);
564  ecef_M_enu[index_1][index_0] = cos(lon);
565  ecef_M_enu[index_1][index_1] = -sin(lat) * sin(lon);
566  ecef_M_enu[index_1][index_2] = cos(lat) * sin(lon);
567  ecef_M_enu[index_1][index_3] = radius * sin(lon) * cos(lat);
568  ecef_M_enu[index_2][index_0] = 0;
569  ecef_M_enu[index_2][index_1] = cos(lat);
570  ecef_M_enu[index_2][index_2] = sin(lat);
571  ecef_M_enu[index_2][index_3] = radius * sin(lat);
572 
573  return ecef_M_enu;
574 }
575 
590 std::vector<std::pair<double, double> > vpMath::computeRegularPointsOnSphere(unsigned int maxPoints)
591 {
592  assert(maxPoints > 0);
593 
594  double a = (4.0 * M_PI) / maxPoints;
595  double d = sqrt(a);
596  int m_theta = static_cast<int>(round(M_PI / d));
597  double d_theta = M_PI / m_theta;
598  double d_phi = a / d_theta;
599 
600  std::vector<std::pair<double, double> > lonlat_vec;
601 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
602  lonlat_vec.reserve(static_cast<unsigned int>(std::sqrt(maxPoints)));
603 #else
604  lonlat_vec.reserve(static_cast<unsigned int>(std::sqrt(static_cast<double>(maxPoints))));
605 #endif
606 
607  for (int m = 0; m < m_theta; ++m) {
608  double theta = (M_PI * (m + 0.5)) / m_theta;
609  int m_phi = static_cast<int>(round((2.0 * M_PI * sin(theta)) / d_phi));
610 
611  for (int n = 0; n < m_phi; ++n) {
612  double phi = (2.0 * M_PI * n) / m_phi;
613  double lon = phi;
614  double lat = M_PI_2 - theta;
615  lonlat_vec.push_back(std::make_pair(deg(lon), deg(lat)));
616  }
617  }
618 
619  return lonlat_vec;
620 }
621 
641 std::vector<vpHomogeneousMatrix> vpMath::getLocalTangentPlaneTransformations(const std::vector<std::pair<double, double> > &lonlatVec, double radius,
642  LongLattToHomogeneous toECEF)
643 {
644  std::vector<vpHomogeneousMatrix> ecef_M_local_vec;
645  ecef_M_local_vec.reserve(lonlatVec.size());
646  size_t lonlatVec_size = lonlatVec.size();
647  for (size_t i = 0; i < lonlatVec_size; ++i) {
648  double lonDeg = lonlatVec[i].first;
649  double latDeg = lonlatVec[i].second;
650 
651  vpHomogeneousMatrix ecef_M_local = toECEF(lonDeg, latDeg, radius);
652  ecef_M_local_vec.push_back(ecef_M_local);
653  }
654  return ecef_M_local_vec;
655 }
656 
678 {
679  assert(from.size() == 3);
680  assert(to.size() == 3);
681  assert(tmp.size() == 3);
682  vpColVector forward = (from - to).normalize();
683  vpColVector right = vpColVector::crossProd(tmp.normalize(), forward).normalize();
684  vpColVector up = vpColVector::crossProd(forward, right).normalize();
685  const unsigned int index_0 = 0;
686  const unsigned int index_1 = 1;
687  const unsigned int index_2 = 2;
688  const unsigned int index_3 = 3;
689 
691  wMc[index_0][index_0] = right[index_0];
692  wMc[index_0][index_1] = up[index_0];
693  wMc[index_0][index_2] = forward[index_0];
694  wMc[index_0][index_3] = from[index_0];
695  wMc[index_1][index_0] = right[index_1];
696  wMc[index_1][index_1] = up[index_1];
697  wMc[index_1][index_2] = forward[index_1];
698  wMc[index_1][index_3] = from[index_1];
699  wMc[index_2][index_0] = right[index_2];
700  wMc[index_2][index_1] = up[index_2];
701  wMc[index_2][index_2] = forward[index_2];
702  wMc[index_2][index_3] = from[index_2];
703 
704  return wMc;
705 }
706 
714 {
715  if (r.size() == 4) {
716  throw(vpException(vpException::fatalError, "Cannot convert angles of a quaternion vector in degrees!"));
717  }
718  vpColVector r_deg(r.size());
719  unsigned int r_size = r.size();
720  for (unsigned int i = 0; i < r_size; ++i) {
721  r_deg[i] = vpMath::deg(r[i]);
722  }
723  return r_deg;
724 }
725 
733 {
734  vpColVector r_deg(r.size());
735  unsigned int r_size = r.size();
736  for (unsigned int i = 0; i < r_size; ++i) {
737  r_deg[i] = vpMath::deg(r[i]);
738  }
739  return r_deg;
740 }
741 
749 {
750  vpColVector r_rad(r.size());
751  unsigned int r_size = r.size();
752  for (unsigned int i = 0; i < r_size; ++i) {
753  r_rad[i] = vpMath::rad(r[i]);
754  }
755  return r_rad;
756 }
757 
764 {
765  vpHomogeneousMatrix ned_M_enu;
766  const unsigned int index_0 = 0;
767  const unsigned int index_1 = 1;
768  const unsigned int index_2 = 2;
769  ned_M_enu[index_0][index_0] = 0;
770  ned_M_enu[index_0][index_1] = 1;
771  ned_M_enu[index_1][index_0] = 1;
772  ned_M_enu[index_1][index_1] = 0;
773  ned_M_enu[index_2][index_2] = -1;
774 
775  vpHomogeneousMatrix ned_M = ned_M_enu * enu_M;
776  return ned_M;
777 }
778 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:641
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:677
static vpHomogeneousMatrix enu2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:550
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:409
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:763
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:590
static vpHomogeneousMatrix ned2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:486
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.