Visual Servoing Platform  version 3.6.1 under development (2024-07-27)
testMathUtils.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  * Test additional math functions such as lon-lat generator or look-at function.
32  */
33 
39 #include <visp3/core/vpConfig.h>
40 
41 #ifdef VISP_HAVE_CATCH2
42 
43 #include <visp3/core/vpHomogeneousMatrix.h>
44 #include <visp3/core/vpMath.h>
45 
46 #define CATCH_CONFIG_RUNNER
47 #include <catch.hpp>
48 
49 // #define VERBOSE
50 // #define DEBUG
51 
52 #ifdef DEBUG
53 #include <visp3/core/vpIoTools.h>
54 #endif
55 
56 #ifdef ENABLE_VISP_NAMESPACE
57 using namespace VISP_NAMESPACE_NAME;
58 #endif
59 
60 TEST_CASE("Lon-Lat generator", "[math_lonlat]")
61 {
62  const int lonStart = 0, lonEnd = 360, nlon = 20;
63  const int latStart = 0, latEnd = 90, nLat = 10;
64  std::vector<double> longitudes = vpMath::linspace(lonStart, lonEnd, nlon);
65  std::vector<double> latitudes = vpMath::linspace(latStart, latEnd, nLat);
66  const double radius = 5;
67 
68  std::vector<std::pair<double, double> > lonlatVec;
69  lonlatVec.reserve(longitudes.size() * latitudes.size());
70  for (auto lon : longitudes) {
71  for (auto lat : latitudes) {
72  lonlatVec.emplace_back(lon, lat);
73  }
74  }
75 
76  SECTION("NED")
77  {
78  std::vector<vpHomogeneousMatrix> ecef_M_ned_vec =
80  for (const auto &ecef_M_ned : ecef_M_ned_vec) {
81 #ifdef VERBOSE
82  std::cout << "Lon-Lat ecef_M_ned:\n" << ecef_M_ned << std::endl;
83 #endif
84  CHECK(ecef_M_ned.isValid());
85  CHECK(ecef_M_ned.getRotationMatrix().isARotationMatrix());
86  CHECK(vpMath::equal(ecef_M_ned.getTranslationVector().sumSquare(), radius * radius));
87  }
88 
89 #ifdef DEBUG
90  vpHomogeneousMatrix ned_M_cv;
91  ned_M_cv[0][0] = 0;
92  ned_M_cv[0][1] = -1;
93  ned_M_cv[1][0] = 1;
94  ned_M_cv[1][1] = 0;
95  const std::string folder = "NED/lon-lat/";
97  int i = 0;
98  for (const auto &ecef_M_ned : ecef_M_ned_vec) {
99  std::stringstream buffer;
100  buffer << folder << "ecef_M_cv_" << std::setw(4) << std::setfill('0') << i++ << ".txt";
101  std::string filename = buffer.str();
102  std::ofstream file(filename);
103  if (file.is_open()) {
104  (ecef_M_ned * ned_M_cv).save(file);
105  }
106  }
107 #endif
108  }
109 
110  SECTION("ENU")
111  {
112  std::vector<vpHomogeneousMatrix> ecef_M_enu_vec =
114  for (const auto &ecef_M_enu : ecef_M_enu_vec) {
115 #ifdef VERBOSE
116  std::cout << "Lon-Lat ecef_M_enu:\n" << ecef_M_enu << std::endl;
117 #endif
118  CHECK(ecef_M_enu.isValid());
119  CHECK(ecef_M_enu.getRotationMatrix().isARotationMatrix());
120  CHECK(vpMath::equal(ecef_M_enu.getTranslationVector().sumSquare(), radius * radius));
121 
122 #ifdef DEBUG
123  vpHomogeneousMatrix enu_M_cv;
124  enu_M_cv[1][1] = -1;
125  enu_M_cv[2][2] = -1;
126  const std::string folder = "ENU/lon-lat/";
127  vpIoTools::makeDirectory(folder);
128  int i = 0;
129  for (const auto &ecef_M_enu : ecef_M_enu_vec) {
130  std::stringstream buffer;
131  buffer << folder << "ecef_M_cv_" << std::setw(4) << std::setfill('0') << i++ << ".txt";
132  std::string filename = buffer.str();
133  std::ofstream file(filename);
134  if (file.is_open()) {
135  (ecef_M_enu * enu_M_cv).save(file);
136  }
137  }
138 #endif
139  }
140  }
141 }
142 
143 TEST_CASE("Equidistributed sphere point", "[math_equi_sphere_pts]")
144 {
145  const unsigned int maxPoints = 200;
146  std::vector<std::pair<double, double> > lonlatVec = vpMath::computeRegularPointsOnSphere(maxPoints);
147  const double radius = 5;
148 
149  SECTION("NED")
150  {
151  std::vector<vpHomogeneousMatrix> ecef_M_ned_vec =
153  CHECK(!ecef_M_ned_vec.empty());
154  for (const auto &ecef_M_ned : ecef_M_ned_vec) {
155 #ifdef VERBOSE
156  std::cout << "Equidistributed ecef_M_ned:\n" << ecef_M_ned << std::endl;
157 #endif
158  CHECK(ecef_M_ned.isValid());
159  CHECK(ecef_M_ned.getRotationMatrix().isARotationMatrix());
160  CHECK(vpMath::equal(ecef_M_ned.getTranslationVector().sumSquare(), radius * radius));
161  }
162 
163 #ifdef DEBUG
164  vpHomogeneousMatrix ned_M_cv;
165  ned_M_cv[0][0] = 0;
166  ned_M_cv[0][1] = -1;
167  ned_M_cv[1][0] = 1;
168  ned_M_cv[1][1] = 0;
169  const std::string folder = "NED/equi/";
170  vpIoTools::makeDirectory(folder);
171  int i = 0;
172  for (const auto &ecef_M_ned : ecef_M_ned_vec) {
173  std::stringstream buffer;
174  buffer << folder << "ecef_M_cv_" << std::setw(4) << std::setfill('0') << i++ << ".txt";
175  std::string filename = buffer.str();
176  std::ofstream file(filename);
177  if (file.is_open()) {
178  (ecef_M_ned * ned_M_cv).save(file);
179  }
180  }
181 #endif
182  }
183 
184  SECTION("ENU")
185  {
186  std::vector<vpHomogeneousMatrix> ecef_M_enu_vec =
188  CHECK(!ecef_M_enu_vec.empty());
189  for (const auto &ecef_M_enu : ecef_M_enu_vec) {
190 #ifdef VERBOSE
191  std::cout << "Equidistributed ecef_M_enu:\n" << ecef_M_enu << std::endl;
192 #endif
193  CHECK(ecef_M_enu.isValid());
194  CHECK(ecef_M_enu.getRotationMatrix().isARotationMatrix());
195  CHECK(vpMath::equal(ecef_M_enu.getTranslationVector().sumSquare(), radius * radius));
196  }
197 
198 #ifdef DEBUG
199  vpHomogeneousMatrix enu_M_cv;
200  enu_M_cv[1][1] = -1;
201  enu_M_cv[2][2] = -1;
202  const std::string folder = "ENU/equi/";
203  vpIoTools::makeDirectory(folder);
204  int i = 0;
205  for (const auto &ecef_M_enu : ecef_M_enu_vec) {
206  std::stringstream buffer;
207  buffer << folder << "ecef_M_cv_" << std::setw(4) << std::setfill('0') << i++ << ".txt";
208  std::string filename = buffer.str();
209  std::ofstream file(filename);
210  if (file.is_open()) {
211  (ecef_M_enu * enu_M_cv).save(file);
212  }
213  }
214 #endif
215  }
216 }
217 
218 TEST_CASE("Look-at", "[math_look_at]")
219 {
220  // Set camera to an arbitrary pose (only translation)
221  vpColVector from_blender = { 8.867762565612793, -1.1965436935424805, 2.1211400032043457 };
222  // Transformation from OpenGL to Blender frame
223  vpHomogeneousMatrix blender_M_gl;
224  blender_M_gl[0][0] = 0;
225  blender_M_gl[0][2] = 1;
226  blender_M_gl[1][0] = 1;
227  blender_M_gl[1][1] = 0;
228  blender_M_gl[2][1] = 1;
229  blender_M_gl[2][2] = 0;
230 
231  // From is the current camera pose expressed in the OpenGL coordinate system
232  vpColVector from = (blender_M_gl.getRotationMatrix().t() * from_blender);
233  // To is the desired point toward the camera must look
234  vpColVector to = { 0, 0, 0 };
235  // Up is an arbitrary vector
236  vpColVector up = { 0, 1, 0 };
237 
238  // Compute the look-at transformation
239  vpHomogeneousMatrix gl_M_cam = vpMath::lookAt(from, to, up);
240  std::cout << "\ngl_M_cam:\n" << gl_M_cam << std::endl;
241 
242  // Transformation from the computer vision frame to the Blender camera frame
243  vpHomogeneousMatrix cam_M_cv;
244  cam_M_cv[1][1] = -1;
245  cam_M_cv[2][2] = -1;
246  // Transformation from the computer vision frame to the Blender frame
247  vpHomogeneousMatrix bl_M_cv = blender_M_gl * gl_M_cam * cam_M_cv;
248  std::cout << "\nbl_M_cv:\n" << bl_M_cv << std::endl;
249 
250  // Ground truth using Blender look-at
251  vpHomogeneousMatrix bl_M_cv_gt = vpHomogeneousMatrix({ 0.13372008502483368, 0.22858507931232452, -0.9642965197563171,
252  8.867762565612793, 0.9910191297531128, -0.030843468382954597,
253  0.13011434674263, -1.1965436935424805, -5.4016709327697754e-08,
254  -0.9730352163314819, -0.23065657913684845, 2.121140241622925 });
255  std::cout << "\nbl_M_cv_gt:\n" << bl_M_cv_gt << std::endl;
256 
257  const double tolerance = 1e-6;
258  for (unsigned int i = 0; i < 3; i++) {
259  for (unsigned int j = 0; j < 4; j++) {
260  CHECK(vpMath::equal(bl_M_cv[i][j], bl_M_cv_gt[i][j], tolerance));
261  }
262  }
263 }
264 
265 int main(int argc, char *argv[])
266 {
267  Catch::Session session; // There must be exactly one instance
268 
269  // Let Catch (using Clara) parse the command line
270  session.applyCommandLine(argc, argv);
271 
272  int numFailed = session.run();
273 
274  // numFailed is clamped to 255 as some unices only use the lower 8 bits.
275  // This clamping has already been applied, so just return it here
276  // You can also do any post run clean-up here
277  return numFailed;
278 }
279 #else
280 #include <iostream>
281 
282 int main() { return EXIT_SUCCESS; }
283 #endif
Implementation of column vector and the associated operations.
Definition: vpColVector.h:191
Implementation of an homogeneous matrix and operations on such kind of matrices.
vpRotationMatrix getRotationMatrix() const
static void makeDirectory(const std::string &dirname)
Definition: vpIoTools.cpp:550
static std::vector< vpHomogeneousMatrix > getLocalTangentPlaneTransformations(const std::vector< std::pair< double, double > > &lonlatVec, double radius, LongLattToHomogeneous func)
Definition: vpMath.cpp:641
static vpHomogeneousMatrix lookAt(const vpColVector &from, const vpColVector &to, vpColVector tmp)
Definition: vpMath.cpp:677
static bool equal(double x, double y, double threshold=0.001)
Definition: vpMath.h:458
static vpHomogeneousMatrix enu2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:550
static std::vector< std::pair< double, double > > computeRegularPointsOnSphere(unsigned int maxPoints)
Definition: vpMath.cpp:590
static std::vector< double > linspace(T start_in, T end_in, unsigned int num_in)
Definition: vpMath.h:332
static vpHomogeneousMatrix ned2ecef(double lonDeg, double latDeg, double radius)
Definition: vpMath.cpp:486
vpRotationMatrix t() const