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