GCC Code Coverage Report


Directory: ./
File: libs/geometry/vision/helper.cpp
Date: 2024-04-19 18:48:28
Exec Total Coverage
Lines: 96 154 62.3%
Branches: 51 238 21.4%

Line Branch Exec Source
1 /************************************************************************
2 *
3 * Copyright (C) 2017-2023 IRCAD France
4 * Copyright (C) 2017-2021 IHU Strasbourg
5 *
6 * This file is part of Sight.
7 *
8 * Sight is free software: you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * Sight is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
20 *
21 ***********************************************************************/
22
23 #include "helper.hpp"
24
25 #include "geometry/vision/detail/reprojection_error.hpp"
26
27 #include <core/spy_log.hpp>
28
29 #include <geometry/eigen/helper.hpp>
30
31 #include <cmath>
32 #include <thread>
33
34 namespace sight::geometry::vision::helper
35 {
36
37 //-----------------------------------------------------------------------------
38
39 3 error_and_points_t compute_reprojection_error(
40 const std::vector<cv::Point3f>& _object_points,
41 const std::vector<cv::Point2f>& _image_points,
42 const cv::Mat& _rvecs,
43 const cv::Mat& _tvecs,
44 const cv::Mat& _camera_matrix,
45 const cv::Mat& _dist_coeffs
46 )
47 {
48
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 error_and_points_t error_and_projected_points;
49 3 std::vector<cv::Point2f> image_points2;
50 3 int total_points = 0;
51 3 double total_err = 0;
52 3 double err = NAN;
53
54 //projection
55
4/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
6 cv::projectPoints(
56
2/4
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
6 cv::Mat(_object_points),
57 _rvecs,
58 _tvecs,
59 _camera_matrix,
60 _dist_coeffs,
61 image_points2
62 );
63
64
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 error_and_projected_points.second = image_points2;
65
66 //difference
67
4/8
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 3 times.
✗ Branch 11 not taken.
3 err = cv::norm(cv::Mat(_image_points), cv::Mat(image_points2), cv::NORM_L2);
68
69 3 int n = static_cast<int>(_object_points.size());
70 3 total_err += err * err;
71 3 total_points += n;
72
73 3 error_and_projected_points.first = std::sqrt(total_err / total_points);
74
75 3 return error_and_projected_points;
76 3 }
77
78 //-----------------------------------------------------------------------------
79
80 cv::Matx44f camera_pose_monocular(
81 const std::vector<cv::Point3f>& _object_points,
82 const std::vector<cv::Point2f>& _image_points,
83 const cv::Mat _camera_matrix,
84 const cv::Mat& _dist_coeffs,
85 const int _flag
86 )
87 {
88 SIGHT_ASSERT(
89 "There should be the same number of 3d points than 2d points",
90 _object_points.size() == _image_points.size()
91 );
92
93 cv::Mat rvec;
94 cv::Mat tvec;
95 cv::Mat r;
96 cv::Mat t;
97 t = cv::Mat::eye(4, 4, CV_64F);
98
99 //solvePnP
100 cv::solvePnP(_object_points, _image_points, _camera_matrix, _dist_coeffs, rvec, tvec, false, _flag);
101
102 // to matrix
103 cv::Rodrigues(rvec, r); // R is 3x3
104
105 t(cv::Range(0, 3), cv::Range(0, 3)) = r * 1; // copies R into T
106 t(cv::Range(0, 3), cv::Range(3, 4)) = tvec * 1; // copies tvec into T
107
108 return cv::Matx44f(t);
109 }
110
111 //-----------------------------------------------------------------------------
112
113 cv::Matx44f camera_pose_stereo(
114 const std::vector<cv::Point3f>& _object_points,
115 const cv::Mat& _camera_matrix1,
116 const cv::Mat& _dist_coeffs1,
117 const cv::Mat& _camera_matrix2,
118 const cv::Mat& _dist_coeffs2,
119 const std::vector<cv::Point2f>& _img_points1,
120 const std::vector<cv::Point2f>& _img_points2,
121 const cv::Mat& _r,
122 const cv::Mat& _t
123 )
124 {
125 //1. initialize solution with solvePnP
126 cv::Mat rvec;
127 cv::Mat tvec;
128 cv::Mat r;
129 cv::Mat t;
130 t = cv::Mat::eye(4, 4, CV_64F);
131
132 cv::Mat extrinsic = cv::Mat::eye(4, 4, CV_64F);
133
134 extrinsic(cv::Range(0, 3), cv::Range(0, 3)) = _r * 1;
135 extrinsic(cv::Range(0, 3), cv::Range(3, 4)) = _t * 1;
136
137 cv::solvePnP(
138 _object_points,
139 _img_points1,
140 _camera_matrix1,
141 _dist_coeffs1,
142 rvec,
143 tvec,
144 false,
145 cv::SOLVEPNP_ITERATIVE
146 );
147
148 std::vector<double> optim_vector = {{
149 rvec.at<double>(0), rvec.at<double>(1), rvec.at<double>(2),
150 tvec.at<double>(0), tvec.at<double>(1), tvec.at<double>(2)
151 }
152 };
153
154 //2. Creation of ceres problem
155 //Minimization of sum of reprojection error for each points in each images
156
157 ::ceres::Problem problem;
158
159 //Cost function for image 1
160 for(std::size_t i = 0 ; i < _img_points1.size() ; ++i)
161 {
162 ::ceres::CostFunction* cost_function = detail::reprojection_error::create(
163 _camera_matrix1,
164 _dist_coeffs1,
165 _img_points1[i],
166 _object_points[i],
167 cv::Mat::eye(4, 4, CV_64F)
168 );
169 problem.AddResidualBlock(
170 cost_function,
171 nullptr,
172 optim_vector.data()
173 );
174 }
175
176 //image 2
177 for(std::size_t i = 0 ; i < _img_points2.size() ; ++i)
178 {
179 ::ceres::CostFunction* cost_function = detail::reprojection_error::create(
180 _camera_matrix2,
181 _dist_coeffs2,
182 _img_points2[i],
183 _object_points[i],
184 extrinsic
185 );
186 problem.AddResidualBlock(
187 cost_function,
188 nullptr,
189 optim_vector.data()
190 );
191 }
192
193 ::ceres::Solver::Options options;
194 options.linear_solver_type = ::ceres::SPARSE_NORMAL_CHOLESKY;
195 options.trust_region_strategy_type = ::ceres::LEVENBERG_MARQUARDT;
196 options.minimizer_progress_to_stdout = false;
197 options.gradient_tolerance = 1e-8;
198 options.function_tolerance = 1e-8;
199 options.max_num_iterations = 100;
200
201 int numthreads = static_cast<int>(std::thread::hardware_concurrency() / 2);
202
203 options.num_threads = numthreads;
204
205 ::ceres::Solver::Summary summary;
206 ::ceres::Solve(options, &problem, &summary);
207
208 SIGHT_DEBUG("Ceres report : " + summary.FullReport());
209
210 cv::Mat final_r_vec = (cv::Mat_<double>(3, 1) << optim_vector[0], optim_vector[1], optim_vector[2]);
211 cv::Mat final_t_vec = (cv::Mat_<double>(3, 1) << optim_vector[3], optim_vector[4], optim_vector[5]);
212
213 cv::Rodrigues(final_r_vec, r); //Rotation vec. to matrix
214
215 t(cv::Range(0, 3), cv::Range(0, 3)) = r * 1; // copies R into T
216 t(cv::Range(0, 3), cv::Range(3, 4)) = final_t_vec * 1; // copies tvec into T
217
218 return cv::Matx44f(t);
219 }
220
221 //-----------------------------------------------------------------------------
222
223 2 void calibrate_pointing_tool(
224 const data::vector::csptr _matrices_vector,
225 data::matrix4::sptr _calibration_matrix,
226 data::matrix4::sptr _center_matrix
227 )
228 {
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(_matrices_vector->size() < 4)
230 {
231 SIGHT_WARN("Number of points when computing the tool calibration should be more than 5.");
232 return;
233 }
234
235 2 geometry::eigen::helper::EigenMatrix matrix_sum;
236 2 matrix_sum.fill(0.);
237 2 Eigen::Vector4d vector_sum;
238 2 vector_sum.fill(0);
239
240
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 2 times.
82 for(const auto& i : *_matrices_vector)
241 {
242 80 data::matrix4::csptr m1 = std::dynamic_pointer_cast<data::matrix4>(i);
243 80 SIGHT_ASSERT("This element of the vector is not a data::matrix4", m1);
244 80 geometry::eigen::helper::EigenMatrix xyz1;
245 80 xyz1.fill(0.);
246 80 xyz1(0, 0) = (*m1)(0, 3);
247 80 xyz1(0, 1) = (*m1)(1, 3);
248 80 xyz1(0, 2) = (*m1)(2, 3);
249 80 xyz1(0, 3) = 1.0;
250
251 80 matrix_sum = matrix_sum + xyz1.transpose() * xyz1;
252
2/6
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
80 vector_sum = vector_sum + xyz1.squaredNorm() * Eigen::Vector4d(xyz1(0, 0), xyz1(0, 1), xyz1(0, 2), xyz1(0, 3));
253 80 }
254
255 2 geometry::eigen::helper::EigenMatrix temp_matrix;
256 2 temp_matrix.fill(0.);
257 2 temp_matrix(0, 0) = vector_sum[0];
258 2 temp_matrix(0, 1) = vector_sum[1];
259 2 temp_matrix(0, 2) = vector_sum[2];
260 2 temp_matrix(0, 3) = vector_sum[3];
261 2 temp_matrix = -temp_matrix* matrix_sum.inverse();
262
263 2 const double a = -1. * temp_matrix(0, 0) / 2.;
264 2 const double b = -1. * temp_matrix(0, 1) / 2.;
265 2 const double c = -1. * temp_matrix(0, 2) / 2.;
266
267 2 Eigen::Vector3d translation;
268 2 translation.fill(0);
269
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 2 times.
82 for(const auto& i : *_matrices_vector)
270 {
271
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 data::matrix4::csptr m1 = std::dynamic_pointer_cast<data::matrix4>(i);
272 80 SIGHT_ASSERT("This element of the vector is not a data::matrix4", m1);
273
2/4
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
160 const geometry::eigen::helper::EigenMatrix point_matrix = geometry::eigen::helper::to_eigen(m1);
274
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 geometry::eigen::helper::EigenMatrix center_matrix(point_matrix);
275
1/2
✓ Branch 1 taken 80 times.
✗ Branch 2 not taken.
80 const geometry::eigen::helper::EigenMatrix point_matrix_inverse = point_matrix.inverse();
276
277 80 center_matrix(0, 3) = a;
278 80 center_matrix(1, 3) = b;
279 80 center_matrix(2, 3) = c;
280
281 80 const geometry::eigen::helper::EigenMatrix calibration_matrix = point_matrix_inverse * center_matrix;
282
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 translation(0) += calibration_matrix(0, 3);
283 80 translation(1) += calibration_matrix(1, 3);
284
1/2
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
80 translation(2) += calibration_matrix(2, 3);
285 80 }
286
287 2 translation /= static_cast<double>(_matrices_vector->size());
288
289 2 (*_calibration_matrix)(0, 3) = translation(0);
290 2 (*_calibration_matrix)(1, 3) = translation(1);
291 2 (*_calibration_matrix)(2, 3) = translation(2);
292
293 2 (*_center_matrix)(0, 3) = a;
294 2 (*_center_matrix)(1, 3) = b;
295 2 (*_center_matrix)(2, 3) = c;
296 }
297
298 //-----------------------------------------------------------------------------
299
300 391 data::point_list::sptr detect_chessboard(
301 const cv::Mat& _img,
302 std::size_t _x_dim,
303 std::size_t _y_dim,
304 float _scale
305 )
306 {
307 391 data::point_list::sptr pointlist;
308
309 391 SIGHT_ASSERT("Expected 8bit pixel components, this image has: " << 8 * _img.elemSize1(), _img.elemSize1() == 1);
310
311 // Ensure that we have a true depth-less 2D image.
312
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 391 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 6 taken 391 times.
✗ Branch 7 not taken.
391 const cv::Mat img2d = _img.dims == 3 ? _img.reshape(0, 2, _img.size + 1) : _img;
313
314 391 cv::Mat gray_img;
315
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 389 times.
391 if(_img.channels() == 1)
316 {
317
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 gray_img = img2d;
318 }
319 else
320 {
321
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 389 times.
389 const auto cvt_method = _img.channels() == 3 ? cv::COLOR_RGB2GRAY : cv::COLOR_RGBA2GRAY;
322
323
1/2
✓ Branch 1 taken 389 times.
✗ Branch 2 not taken.
389 cv::cvtColor(img2d, gray_img, cvt_method);
324 }
325
326 391 const cv::Size board_size(static_cast<int>(_x_dim) - 1, static_cast<int>(_y_dim) - 1);
327 391 std::vector<cv::Point2f> corners;
328
329 391 const int flags = cv::CALIB_CB_ADAPTIVE_THRESH | cv::CALIB_CB_NORMALIZE_IMAGE | cv::CALIB_CB_FILTER_QUADS
330 | cv::CALIB_CB_FAST_CHECK;
331
332 391 cv::Mat detection_image;
333
334
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 268 times.
391 if(_scale < 1.F)
335 {
336
1/2
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
123 cv::resize(gray_img, detection_image, cv::Size(), _scale, _scale);
337 }
338 else
339 {
340
1/2
✓ Branch 1 taken 268 times.
✗ Branch 2 not taken.
268 detection_image = gray_img;
341 }
342
343
3/4
✓ Branch 1 taken 391 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
✓ Branch 4 taken 268 times.
391 const bool pattern_was_found = cv::findChessboardCorners(detection_image, board_size, corners, flags);
344
345
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 268 times.
391 if(pattern_was_found)
346 {
347 // Rescale points to get their coordinates in the full scale image.
348 7634 const auto rescale = [_scale](cv::Point2f& _pt){_pt = _pt / _scale;};
349 123 std::ranges::for_each(corners, rescale);
350
351 // Refine points coordinates in the full scale image.
352
1/2
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
123 cv::TermCriteria term(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 30, 0.1);
353
2/4
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 123 times.
✗ Branch 5 not taken.
123 cv::cornerSubPix(gray_img, corners, cv::Size(5, 5), cv::Size(-1, -1), term);
354
355
2/4
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 123 times.
246 pointlist = std::make_shared<data::point_list>();
356
1/2
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
123 data::point_list::container_t& points = pointlist->get_points();
357
1/2
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
123 points.reserve(corners.size());
358
359
1/2
✓ Branch 2 taken 7634 times.
✗ Branch 3 not taken.
7634 const auto cv2_sight_pt = [](const cv::Point2f& _p){return std::make_shared<data::point>(_p.x, _p.y);};
360
1/2
✓ Branch 1 taken 123 times.
✗ Branch 2 not taken.
123 std::ranges::transform(corners, std::back_inserter(points), cv2_sight_pt);
361 }
362
363 391 return pointlist;
364 391 }
365
366 // ----------------------------------------------------------------------------
367
368 } // namespace sight::geometry::vision::helper
369