Visual Servoing Platform  version 3.6.1 under development (2024-04-19)
vpMath.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * Simple mathematical function not available in the C math library (math.h).
33  *
34 *****************************************************************************/
35 
42 #include <cmath>
43 #include <functional>
44 #include <numeric>
45 #include <stdint.h>
46 #include <cassert>
47 #include <ctype.h>
48 
49 #include <visp3/core/vpException.h>
50 #include <visp3/core/vpMath.h>
51 #include <visp3/core/vpMatrix.h>
52 
53 #if defined(VISP_HAVE_FUNC__FINITE)
54 #include <float.h>
55 #endif
56 
57 #if !(defined(VISP_HAVE_FUNC_ISNAN) || defined(VISP_HAVE_FUNC_STD_ISNAN)) || \
58  !(defined(VISP_HAVE_FUNC_ISINF) || defined(VISP_HAVE_FUNC_STD_ISINF)) || \
59  !(defined(VISP_HAVE_FUNC_ISFINITE) || defined(VISP_HAVE_FUNC_STD_ISFINITE) || defined(VISP_HAVE_FUNC__FINITE))
60 #if defined _MSC_VER || defined __BORLANDC__
61 typedef __int64 int64;
62 typedef unsigned __int64 uint64;
63 #else
64 typedef int64_t int64;
65 typedef uint64_t uint64;
66 #endif
67 
68 #ifndef DOXYGEN_SHOULD_SKIP_THIS
69 typedef union Vp64suf
70 {
71  // int64 i; //Unused variable, should be harmless to comment it
72  uint64 u;
73  double f;
74 } Vp64suf;
75 
76 typedef union Vp32suf
77 {
78  // int i; //Unused variable, should be harmless to comment it
79  unsigned u;
80  float f;
81 } Vp32suf;
82 #endif
83 #endif
84 
85 const double vpMath::ang_min_sinc = 1.0e-8;
86 const double vpMath::ang_min_mc = 2.5e-4;
87 
93 bool vpMath::isNaN(double value)
94 {
95 #if defined(VISP_HAVE_FUNC_STD_ISNAN)
96  return std::isnan(value);
97 #elif defined(VISP_HAVE_FUNC_ISNAN)
98  return isnan(value);
99 #elif defined(VISP_HAVE_FUNC__ISNAN)
100  return (_isnan(value) != 0);
101 #else
102  // Taken from OpenCV source code CvIsNan()
103  Vp64suf ieee754;
104  ieee754.f = value;
105  return (((unsigned)(ieee754.u >> 32) & 0x7fffffff) + ((unsigned)ieee754.u != 0) > 0x7ff00000) != 0;
106 #endif
107 }
108 
114 bool vpMath::isNaN(float value)
115 {
116 #if defined(VISP_HAVE_FUNC_STD_ISNAN)
117  return std::isnan(value);
118 #elif defined(VISP_HAVE_FUNC_ISNAN)
119  return isnan(value);
120 #elif defined(VISP_HAVE_FUNC__ISNAN)
121  return (_isnan(value) != 0);
122 #else
123  // Taken from OpenCV source code CvIsNan()
124  Vp32suf ieee754;
125  ieee754.f = value;
126  return ((unsigned)ieee754.u & 0x7fffffff) > 0x7f800000;
127 #endif
128 }
129 
137 bool vpMath::isInf(double value)
138 {
139 #if defined(VISP_HAVE_FUNC_STD_ISINF)
140  return std::isinf(value);
141 #elif defined(VISP_HAVE_FUNC_ISINF)
142  return isinf(value);
143 #else
144  // Taken from OpenCV source code CvIsInf()
145  Vp64suf ieee754;
146  ieee754.f = value;
147  return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && (unsigned)ieee754.u == 0;
148 #endif
149 }
150 
158 bool vpMath::isInf(float value)
159 {
160 #if defined(VISP_HAVE_FUNC_STD_ISINF)
161  return std::isinf(value);
162 #elif defined(VISP_HAVE_FUNC_ISINF)
163  return isinf(value);
164 #else
165  // Taken from OpenCV source code CvIsInf()
166  Vp32suf ieee754;
167  ieee754.f = value;
168  return ((unsigned)ieee754.u & 0x7fffffff) == 0x7f800000;
169 #endif
170 }
171 
178 bool vpMath::isFinite(double value)
179 {
180 #if defined(VISP_HAVE_FUNC_STD_ISFINITE)
181  return std::isfinite(value);
182 #elif defined(VISP_HAVE_FUNC_ISFINITE)
183  return isfinite(value);
184 #elif defined(VISP_HAVE_FUNC__FINITE)
185  return _finite(value);
186 #else
187  return !vpMath::isInf(value) && !vpMath::isNaN(value);
188 #endif
189 }
190 
197 bool vpMath::isFinite(float value)
198 {
199 #if defined(VISP_HAVE_FUNC_STD_ISFINITE)
200  return std::isfinite(value);
201 #elif defined(VISP_HAVE_FUNC_ISFINITE)
202  return isfinite(value);
203 #elif defined(VISP_HAVE_FUNC__FINITE)
204  return _finitef(value);
205 #else
206  return !vpMath::isInf(value) && !vpMath::isNaN(value);
207 #endif
208 }
209 
215 bool vpMath::isNumber(const std::string &str)
216 {
217  size_t str_size = str.size();
218  for (size_t i = 0; i < str_size; ++i) {
219  if (isdigit(str[i]) == false) {
220  return false;
221  }
222  }
223  return true;
224 }
225 
234 double vpMath::mcosc(double cosx, double x)
235 {
236  if (fabs(x) < ang_min_mc) {
237  return 0.5;
238  }
239  else {
240  return ((1.0 - cosx) / x / x);
241  }
242 }
243 
252 double vpMath::msinc(double sinx, double x)
253 {
254  if (fabs(x) < ang_min_mc) {
255  return (1. / 6.0);
256  }
257  else {
258  return ((1.0 - (sinx / x)) / x / x);
259  }
260 }
261 
269 double vpMath::sinc(double x)
270 {
271  if (fabs(x) < ang_min_sinc) {
272  return 1.0;
273  }
274  else {
275  return sin(x) / x;
276  }
277 }
286 double vpMath::sinc(double sinx, double x)
287 {
288  if (fabs(x) < ang_min_sinc) {
289  return 1.0;
290  }
291  else {
292  return (sinx / x);
293  }
294 }
295 
303 double vpMath::getMean(const std::vector<double> &v)
304 {
305  if (v.empty()) {
306  throw vpException(vpException::notInitialized, "Empty vector !");
307  }
308 
309  size_t size = v.size();
310 
311  double sum = std::accumulate(v.begin(), v.end(), 0.0);
312 
313  return sum / (static_cast<double>(size));
314 }
315 
323 double vpMath::getMedian(const std::vector<double> &v)
324 {
325  if (v.empty()) {
326  throw vpException(vpException::notInitialized, "Empty vector !");
327  }
328 
329  std::vector<double> v_copy = v;
330  size_t size = v_copy.size();
331 
332  size_t n = size / 2;
333  std::nth_element(v_copy.begin(), v_copy.begin() + n, v_copy.end());
334  double val_n = v_copy[n];
335 
336  if ((size % 2) == 1) {
337  return val_n;
338  }
339  else {
340  std::nth_element(v_copy.begin(), v_copy.begin() + (n - 1), v_copy.end());
341  return 0.5 * (val_n + v_copy[n - 1]);
342  }
343 }
344 
354 double vpMath::getStdev(const std::vector<double> &v, bool useBesselCorrection)
355 {
356  if (v.empty()) {
357  throw vpException(vpException::notInitialized, "Empty vector !");
358  }
359 
360  double mean = getMean(v);
361 
362  std::vector<double> diff(v.size());
363 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
364  std::transform(v.begin(), v.end(), diff.begin(), std::bind(std::minus<double>(), std::placeholders::_1, mean));
365 #else
366  std::transform(v.begin(), v.end(), diff.begin(), std::bind2nd(std::minus<double>(), mean));
367 #endif
368 
369  double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0);
370  double divisor = static_cast<double> (v.size());
371  if (useBesselCorrection && (v.size() > 1)) {
372  divisor = divisor - 1;
373  }
374 
375  return std::sqrt(sq_sum / divisor);
376 }
377 
391 double vpMath::lineFitting(const std::vector<vpImagePoint> &imPts, double &a, double &b, double &c)
392 {
393  if (imPts.size() < 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 
487 vpHomogeneousMatrix vpMath::ned2ecef(double lonDeg, double latDeg, double radius)
488 {
489  double lon = vpMath::rad(lonDeg);
490  double lat = vpMath::rad(latDeg);
491 
492  vpHomogeneousMatrix ecef_M_ned;
493  ecef_M_ned[0][0] = -sin(lat) * cos(lon);
494  ecef_M_ned[0][1] = -sin(lon);
495  ecef_M_ned[0][2] = -cos(lat) * cos(lon);
496  ecef_M_ned[0][3] = radius * cos(lon) * cos(lat);
497  ecef_M_ned[1][0] = -sin(lat) * sin(lon);
498  ecef_M_ned[1][1] = cos(lon);
499  ecef_M_ned[1][2] = -cos(lat) * sin(lon);
500  ecef_M_ned[1][3] = radius * sin(lon) * cos(lat);
501  ecef_M_ned[2][0] = cos(lat);
502  ecef_M_ned[2][1] = 0;
503  ecef_M_ned[2][2] = -sin(lat);
504  ecef_M_ned[2][3] = radius * sin(lat);
505 
506  return ecef_M_ned;
507 }
508 
548 vpHomogeneousMatrix vpMath::enu2ecef(double lonDeg, double latDeg, double radius)
549 {
550  double lon = vpMath::rad(lonDeg);
551  double lat = vpMath::rad(latDeg);
552 
553  vpHomogeneousMatrix ecef_M_enu;
554  ecef_M_enu[0][0] = -sin(lon);
555  ecef_M_enu[0][1] = -sin(lat) * cos(lon);
556  ecef_M_enu[0][2] = cos(lat) * cos(lon);
557  ecef_M_enu[0][3] = radius * cos(lon) * cos(lat);
558  ecef_M_enu[1][0] = cos(lon);
559  ecef_M_enu[1][1] = -sin(lat) * sin(lon);
560  ecef_M_enu[1][2] = cos(lat) * sin(lon);
561  ecef_M_enu[1][3] = radius * sin(lon) * cos(lat);
562  ecef_M_enu[2][0] = 0;
563  ecef_M_enu[2][1] = cos(lat);
564  ecef_M_enu[2][2] = sin(lat);
565  ecef_M_enu[2][3] = radius * sin(lat);
566 
567  return ecef_M_enu;
568 }
569 
584 std::vector<std::pair<double, double> > vpMath::computeRegularPointsOnSphere(unsigned int maxPoints)
585 {
586  assert(maxPoints > 0);
587 
588  double a = (4.0 * M_PI) / maxPoints;
589  double d = sqrt(a);
590  int m_theta = static_cast<int>(round(M_PI / d));
591  double d_theta = M_PI / m_theta;
592  double d_phi = a / d_theta;
593 
594  std::vector<std::pair<double, double> > lonlat_vec;
595 #if (VISP_CXX_STANDARD > VISP_CXX_STANDARD_98)
596  lonlat_vec.reserve(static_cast<unsigned int>(std::sqrt(maxPoints)));
597 #else
598  lonlat_vec.reserve(static_cast<unsigned int>(std::sqrt(static_cast<double>(maxPoints))));
599 #endif
600 
601  for (int m = 0; m < m_theta; ++m) {
602  double theta = (M_PI * (m + 0.5)) / m_theta;
603  int m_phi = static_cast<int>(round((2.0 * M_PI * sin(theta)) / d_phi));
604 
605  for (int n = 0; n < m_phi; ++n) {
606  double phi = (2.0 * M_PI * n) / m_phi;
607  double lon = phi;
608  double lat = M_PI_2 - theta;
609  lonlat_vec.push_back(std::make_pair(deg(lon), deg(lat)));
610  }
611  }
612 
613  return lonlat_vec;
614 }
615 
635 std::vector<vpHomogeneousMatrix> vpMath::getLocalTangentPlaneTransformations(const std::vector<std::pair<double, double> > &lonlatVec, double radius,
636  vpHomogeneousMatrix(*toECEF)(double lonDeg_, double latDeg_, double radius_))
637 {
638  std::vector<vpHomogeneousMatrix> ecef_M_local_vec;
639  ecef_M_local_vec.reserve(lonlatVec.size());
640  size_t lonlatVec_size = lonlatVec.size();
641  for (size_t i = 0; i < lonlatVec_size; ++i) {
642  double lonDeg = lonlatVec[i].first;
643  double latDeg = lonlatVec[i].second;
644 
645  vpHomogeneousMatrix ecef_M_local = toECEF(lonDeg, latDeg, radius);
646  ecef_M_local_vec.push_back(ecef_M_local);
647  }
648  return ecef_M_local_vec;
649 }
650 
672 {
673  assert(from.size() == 3);
674  assert(to.size() == 3);
675  assert(tmp.size() == 3);
676  vpColVector forward = (from - to).normalize();
677  vpColVector right = vpColVector::crossProd(tmp.normalize(), forward).normalize();
678  vpColVector up = vpColVector::crossProd(forward, right).normalize();
679 
681  wMc[0][0] = right[0];
682  wMc[0][1] = up[0];
683  wMc[0][2] = forward[0];
684  wMc[0][3] = from[0];
685  wMc[1][0] = right[1];
686  wMc[1][1] = up[1];
687  wMc[1][2] = forward[1];
688  wMc[1][3] = from[1];
689  wMc[2][0] = right[2];
690  wMc[2][1] = up[2];
691  wMc[2][2] = forward[2];
692  wMc[2][3] = from[2];
693 
694  return wMc;
695 }
696 
704 {
705  if (r.size() == 4) {
706  throw(vpException(vpException::fatalError, "Cannot convert angles of a quaternion vector in degrees!"));
707  }
708  vpColVector r_deg(r.size());
709  unsigned int r_size = r.size();
710  for (unsigned int i = 0; i < r_size; ++i) {
711  r_deg[i] = vpMath::deg(r[i]);
712  }
713  return r_deg;
714 }
715 
723 {
724  vpColVector r_deg(r.size());
725  unsigned int r_size = r.size();
726  for (unsigned int i = 0; i < r_size; ++i) {
727  r_deg[i] = vpMath::deg(r[i]);
728  }
729  return r_deg;
730 }
731 
739 {
740  vpColVector r_rad(r.size());
741  unsigned int r_size = r.size();
742  for (unsigned int i = 0; i < r_size; ++i) {
743  r_rad[i] = vpMath::rad(r[i]);
744  }
745  return r_rad;
746 }
747 
754 {
755  vpHomogeneousMatrix ned_M_enu;
756  ned_M_enu[0][0] = 0;
757  ned_M_enu[0][1] = 1;
758  ned_M_enu[1][0] = 1;
759  ned_M_enu[1][1] = 0;
760  ned_M_enu[2][2] = -1;
761 
762  vpHomogeneousMatrix ned_M = ned_M_enu * enu_M;
763  return ned_M;
764 }
unsigned int size() const
Return the number of elements of the 2D array.
Definition: vpArray2D.h:339
Implementation of column vector and the associated operations.
Definition: vpColVector.h:163
vpColVector & normalize()
static vpColVector crossProd(const vpColVector &a, const vpColVector &b)
error that can be emitted by ViSP classes.
Definition: vpException.h:59
@ notInitialized
Used to indicate that a parameter is not initialized.
Definition: vpException.h:86
@ dimensionError
Bad dimension.
Definition: vpException.h:83
@ fatalError
Fatal error.
Definition: vpException.h:84
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:252
static bool isNaN(double value)
Definition: vpMath.cpp:93
static std::vector< vpHomogeneousMatrix > getLocalTangentPlaneTransformations(const std::vector< std::pair< double, double > > &lonlatVec, double radius, vpHomogeneousMatrix(*toECEF)(double lonDeg, double latDeg, double radius))
Definition: vpMath.cpp:635
static double rad(double deg)
Definition: vpMath.h:127
static double getMedian(const std::vector< double > &v)
Definition: vpMath.cpp:323
static double sinc(double x)
Definition: vpMath.cpp:269
static double getStdev(const std::vector< double > &v, bool useBesselCorrection=false)
Definition: vpMath.cpp:354
static vpHomogeneousMatrix lookAt(const vpColVector &from, const vpColVector &to, vpColVector tmp)
Definition: vpMath.cpp:671
static vpHomogeneousMatrix enu2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:548
static double lineFitting(const std::vector< vpImagePoint > &imPts, double &a, double &b, double &c)
Definition: vpMath.cpp:391
static int round(double x)
Definition: vpMath.h:403
static bool isFinite(double value)
Definition: vpMath.cpp:178
static double getMean(const std::vector< double > &v)
Definition: vpMath.cpp:303
static bool isInf(double value)
Definition: vpMath.cpp:137
static vpHomogeneousMatrix enu2ned(const vpHomogeneousMatrix &enu_M)
Definition: vpMath.cpp:753
static double mcosc(double cosx, double x)
Definition: vpMath.cpp:234
static double deg(double rad)
Definition: vpMath.h:117
static bool isNumber(const std::string &str)
Definition: vpMath.cpp:215
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:175
static std::vector< std::pair< double, double > > computeRegularPointsOnSphere(unsigned int maxPoints)
Definition: vpMath.cpp:584
static vpHomogeneousMatrix ned2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:487
Implementation of a matrix and operations on matrices.
Definition: vpMatrix.h:146
vpColVector eigenValues() const
Definition: vpMatrix.cpp:5836
Implementation of a generic rotation vector.