Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpCalibration.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 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 * Camera calibration.
32 */
33
38
39#include <visp3/core/vpDebug.h>
40#include <visp3/core/vpPixelMeterConversion.h>
41#include <visp3/vision/vpCalibration.h>
42#include <visp3/vision/vpPose.h>
43
45
46double vpCalibration::m_threshold = 1e-10f;
47unsigned int vpCalibration::m_nbIterMax = 4000;
48double vpCalibration::m_gain = 0.25;
49
51{
52 m_npt = 0;
53
54 m_residual = m_residual_dist = 1000.;
55
56 m_LoX.clear();
57 m_LoY.clear();
58 m_LoZ.clear();
59 m_Lip.clear();
60
61 return 0;
62}
63
65 : cMo(), cMo_dist(), cam(), cam_dist(), rMe(), eMc(), eMc_dist(), m_aspect_ratio(-1), m_npt(0),
66 m_LoX(), m_LoY(), m_LoZ(), m_Lip(), m_residual(1000.), m_residual_dist(1000.)
67
68{
69 init();
70}
71
73 : cMo(), cMo_dist(), cam(), cam_dist(), rMe(), eMc(), eMc_dist(), m_aspect_ratio(-1), m_npt(0),
74 m_LoX(), m_LoY(), m_LoZ(), m_Lip(), m_residual(1000.), m_residual_dist(1000.)
75{
76 (*this) = c;
77}
78
80
82{
83 m_npt = twinCalibration.m_npt;
84 m_LoX = twinCalibration.m_LoX;
85 m_LoY = twinCalibration.m_LoY;
86 m_LoZ = twinCalibration.m_LoZ;
87 m_Lip = twinCalibration.m_Lip;
88
89 m_residual = twinCalibration.m_residual;
90 cMo = twinCalibration.cMo;
91 m_residual_dist = twinCalibration.m_residual_dist;
92 cMo_dist = twinCalibration.cMo_dist;
93
94 cam = twinCalibration.cam;
95 cam_dist = twinCalibration.cam_dist;
96
97 rMe = twinCalibration.rMe;
98
99 eMc = twinCalibration.eMc;
100 eMc_dist = twinCalibration.eMc_dist;
101
102 m_aspect_ratio = twinCalibration.m_aspect_ratio;
103
104 return (*this);
105}
106
108{
109 m_LoX.clear();
110 m_LoY.clear();
111 m_LoZ.clear();
112 m_Lip.clear();
113 m_npt = 0;
114
115 return 0;
116}
117
118int vpCalibration::addPoint(double X, double Y, double Z, vpImagePoint &ip)
119{
120 m_LoX.push_back(X);
121 m_LoY.push_back(Y);
122 m_LoZ.push_back(Z);
123
124 m_Lip.push_back(ip);
125
126 m_npt++;
127
128 return 0;
129}
130
136void vpCalibration::computePose(const vpCameraParameters &camera, vpHomogeneousMatrix &cMo_est)
137{
138 // The vpPose class mainly contents a list of vpPoint (that is (X,Y,Z, x, y)
139 // )
140 vpPose pose;
141 // the list of point is cleared (if that's not done before)
142 pose.clearPoint();
143 // we set the 3D points coordinates (in meter !) in the object/world frame
144 std::list<double>::const_iterator it_LoX = m_LoX.begin();
145 std::list<double>::const_iterator it_LoY = m_LoY.begin();
146 std::list<double>::const_iterator it_LoZ = m_LoZ.begin();
147 std::list<vpImagePoint>::const_iterator it_Lip = m_Lip.begin();
148
149 for (unsigned int i = 0; i < m_npt; i++) {
150 vpPoint P(*it_LoX, *it_LoY, *it_LoZ);
151 double x = 0, y = 0;
152 vpPixelMeterConversion::convertPoint(camera, *it_Lip, x, y);
153 P.set_x(x);
154 P.set_y(y);
155
156 pose.addPoint(P);
157 ++it_LoX;
158 ++it_LoY;
159 ++it_LoZ;
160 ++it_Lip;
161 }
162
163 // the pose is now refined using the virtual visual servoing approach
164 // Warning: cMo needs to be initialized otherwise it may diverge
166}
167
169{
170 m_residual = 0;
171
172 std::list<double>::const_iterator it_LoX = m_LoX.begin();
173 std::list<double>::const_iterator it_LoY = m_LoY.begin();
174 std::list<double>::const_iterator it_LoZ = m_LoZ.begin();
175 std::list<vpImagePoint>::const_iterator it_Lip = m_Lip.begin();
176
177 double u0 = camera.get_u0();
178 double v0 = camera.get_v0();
179 double px = camera.get_px();
180 double py = camera.get_py();
181 vpImagePoint ip;
182
183 for (unsigned int i = 0; i < m_npt; i++) {
184 double oX = *it_LoX;
185 double oY = *it_LoY;
186 double oZ = *it_LoZ;
187
188 double cX = oX * cMo_est[0][0] + oY * cMo_est[0][1] + oZ * cMo_est[0][2] + cMo_est[0][3];
189 double cY = oX * cMo_est[1][0] + oY * cMo_est[1][1] + oZ * cMo_est[1][2] + cMo_est[1][3];
190 double cZ = oX * cMo_est[2][0] + oY * cMo_est[2][1] + oZ * cMo_est[2][2] + cMo_est[2][3];
191
192 double x = cX / cZ;
193 double y = cY / cZ;
194
195 ip = *it_Lip;
196 double u = ip.get_u();
197 double v = ip.get_v();
198
199 double xp = u0 + x * px;
200 double yp = v0 + y * py;
201
202 m_residual += (vpMath::sqr(xp - u) + vpMath::sqr(yp - v));
203
204 ++it_LoX;
205 ++it_LoY;
206 ++it_LoZ;
207 ++it_Lip;
208 }
209 return sqrt(m_residual / m_npt);
210}
211
213{
214 m_residual_dist = 0;
215
216 std::list<double>::const_iterator it_LoX = m_LoX.begin();
217 std::list<double>::const_iterator it_LoY = m_LoY.begin();
218 std::list<double>::const_iterator it_LoZ = m_LoZ.begin();
219 std::list<vpImagePoint>::const_iterator it_Lip = m_Lip.begin();
220
221 double u0 = camera.get_u0();
222 double v0 = camera.get_v0();
223 double px = camera.get_px();
224 double py = camera.get_py();
225 double kud = camera.get_kud();
226 double kdu = camera.get_kdu();
227
228 double inv_px = 1 / px;
229 double inv_py = 1 / px;
230 vpImagePoint ip;
231
232 for (unsigned int i = 0; i < m_npt; i++) {
233 double oX = *it_LoX;
234 double oY = *it_LoY;
235 double oZ = *it_LoZ;
236
237 double cX = oX * cMo_est[0][0] + oY * cMo_est[0][1] + oZ * cMo_est[0][2] + cMo_est[0][3];
238 double cY = oX * cMo_est[1][0] + oY * cMo_est[1][1] + oZ * cMo_est[1][2] + cMo_est[1][3];
239 double cZ = oX * cMo_est[2][0] + oY * cMo_est[2][1] + oZ * cMo_est[2][2] + cMo_est[2][3];
240
241 double x = cX / cZ;
242 double y = cY / cZ;
243
244 ip = *it_Lip;
245 double u = ip.get_u();
246 double v = ip.get_v();
247
248 double r2ud = 1 + kud * (vpMath::sqr(x) + vpMath::sqr(y));
249
250 double xp = u0 + x * px * r2ud;
251 double yp = v0 + y * py * r2ud;
252
253 m_residual_dist += (vpMath::sqr(xp - u) + vpMath::sqr(yp - v));
254
255 double r2du = (vpMath::sqr((u - u0) * inv_px) + vpMath::sqr((v - v0) * inv_py));
256
257 xp = u0 + x * px - kdu * (u - u0) * r2du;
258 yp = v0 + y * py - kdu * (v - v0) * r2du;
259
260 m_residual_dist += (vpMath::sqr(xp - u) + vpMath::sqr(yp - v));
261 ++it_LoX;
262 ++it_LoY;
263 ++it_LoZ;
264 ++it_Lip;
265 }
266 m_residual_dist /= 2;
267
268 return sqrt(m_residual_dist / m_npt);
269}
270
271void vpCalibration::computeStdDeviation(double &deviation, double &deviation_dist)
272{
273 deviation = computeStdDeviation(cMo, cam);
274 deviation_dist = computeStdDeviation_dist(cMo_dist, cam_dist);
275}
276
278 vpCameraParameters &cam_est, bool verbose)
279{
280 try {
281 computePose(cam_est, cMo_est);
282 switch (method) {
283 case CALIB_LAGRANGE:
285 calibLagrange(cam_est, cMo_est);
286 } break;
287 case CALIB_VIRTUAL_VS:
290 default:
291 break;
292 }
293
294 switch (method) {
295 case CALIB_VIRTUAL_VS:
299 if (verbose) {
300 std::cout << "start calibration without distortion" << std::endl;
301 }
302 calibVVS(cam_est, cMo_est, verbose);
303 } break;
304 case CALIB_LAGRANGE:
305 default:
306 break;
307 }
308 this->cMo = cMo_est;
309 this->cMo_dist = cMo_est;
310
311 // Print camera parameters
312 if (verbose) {
313 // std::cout << "Camera parameters without distortion :" <<
314 // std::endl;
315 cam_est.printParameters();
316 }
317
318 this->cam = cam_est;
319
320 switch (method) {
323 if (verbose) {
324 std::cout << "start calibration with distortion" << std::endl;
325 }
326 calibVVSWithDistortion(cam_est, cMo_est, verbose);
327 } break;
328 case CALIB_LAGRANGE:
329 case CALIB_VIRTUAL_VS:
331 default:
332 break;
333 }
334 // Print camera parameters
335 if (verbose) {
336 // std::cout << "Camera parameters without distortion :" <<
337 // std::endl;
338 this->cam.printParameters();
339 // std::cout << "Camera parameters with distortion :" <<
340 // std::endl;
341 cam_est.printParameters();
342 }
343
344 this->cam_dist = cam_est;
345
346 this->cMo_dist = cMo_est;
347
348 if (cam_est.get_px() < 0 || cam_est.get_py() < 0 || cam_est.get_u0() < 0 || cam_est.get_v0() < 0) {
349 if (verbose) {
350 std::cout << "Unable to calibrate the camera. Estimated parameters are negative." << std::endl;
351 }
352 return EXIT_FAILURE;
353 }
354
355 return EXIT_SUCCESS;
356 }
357 catch (...) {
358 throw;
359 }
360}
361
362int vpCalibration::computeCalibrationMulti(vpCalibrationMethodType method, std::vector<vpCalibration> &table_cal,
363 vpCameraParameters &cam_est, double &globalReprojectionError, bool verbose)
364{
365 try {
366 unsigned int nbPose = static_cast<unsigned int>(table_cal.size());
367 for (unsigned int i = 0; i < nbPose; i++) {
368 if (table_cal[i].get_npt() > 3)
369 table_cal[i].computePose(cam_est, table_cal[i].cMo);
370 }
371 switch (method) {
372 case CALIB_LAGRANGE: {
373 if (nbPose > 1) {
374 std::cout << "this calibration method is not available in" << std::endl
375 << "vpCalibration::computeCalibrationMulti()" << std::endl;
376 return -1;
377 }
378 else {
379 table_cal[0].calibLagrange(cam_est, table_cal[0].cMo);
380 table_cal[0].cam = cam_est;
381 table_cal[0].cam_dist = cam_est;
382 table_cal[0].cMo_dist = table_cal[0].cMo;
383 }
384 break;
385 }
388 if (nbPose > 1) {
389 std::cout << "this calibration method is not available in" << std::endl
390 << "vpCalibration::computeCalibrationMulti()" << std::endl
391 << "with several images." << std::endl;
392 return -1;
393 }
394 else {
395 table_cal[0].calibLagrange(cam_est, table_cal[0].cMo);
396 table_cal[0].cam = cam_est;
397 table_cal[0].cam_dist = cam_est;
398 table_cal[0].cMo_dist = table_cal[0].cMo;
399 }
400 calibVVSMulti(table_cal, cam_est, globalReprojectionError, verbose, table_cal[0].m_aspect_ratio);
401 break;
402 }
403 case CALIB_VIRTUAL_VS:
405 calibVVSMulti(table_cal, cam_est, globalReprojectionError, verbose, table_cal[0].m_aspect_ratio);
406 break;
407 }
408 }
409 // Print camera parameters
410 if (verbose) {
411 // std::cout << "Camera parameters without distortion :" <<
412 // std::endl;
413 cam_est.printParameters();
414 }
415
416 switch (method) {
417 case CALIB_LAGRANGE:
419 case CALIB_VIRTUAL_VS:
420 verbose = false;
421 break;
424 if (verbose)
425 std::cout << "Compute camera parameters with distortion" << std::endl;
426
427 calibVVSWithDistortionMulti(table_cal, cam_est, globalReprojectionError, verbose, table_cal[0].m_aspect_ratio);
428 } break;
429 }
430 // Print camera parameters
431 if (verbose) {
432 // std::cout << "Camera parameters without distortion :" <<
433 // std::endl;
434 table_cal[0].cam.printParameters();
435 // std::cout << "Camera parameters with distortion:" << std::endl;
436 cam_est.printParameters();
437 std::cout << std::endl;
438 }
439
440 if (cam_est.get_px() < 0 || cam_est.get_py() < 0 || cam_est.get_u0() < 0 || cam_est.get_v0() < 0) {
441 if (verbose) {
442 std::cout << "Unable to calibrate the camera. Estimated parameters are negative." << std::endl;
443 }
444 return EXIT_FAILURE;
445 }
446
447 return EXIT_SUCCESS;
448 }
449 catch (...) {
450 throw;
451 }
452}
453
454int vpCalibration::writeData(const std::string &filename)
455{
456 std::ofstream f(filename.c_str());
457 vpImagePoint ip;
458
459 std::list<double>::const_iterator it_LoX = m_LoX.begin();
460 std::list<double>::const_iterator it_LoY = m_LoY.begin();
461 std::list<double>::const_iterator it_LoZ = m_LoZ.begin();
462 std::list<vpImagePoint>::const_iterator it_Lip = m_Lip.begin();
463
464 f.precision(10);
465 f.setf(std::ios::fixed, std::ios::floatfield);
466 f << m_LoX.size() << std::endl;
467
468 for (unsigned int i = 0; i < m_LoX.size(); ++i) {
469
470 double oX = *it_LoX;
471 double oY = *it_LoY;
472 double oZ = *it_LoZ;
473
474 ip = *it_Lip;
475 double u = ip.get_u();
476 double v = ip.get_v();
477
478 f << oX << " " << oY << " " << oZ << " " << u << " " << v << std::endl;
479
480 ++it_LoX;
481 ++it_LoY;
482 ++it_LoZ;
483 ++it_Lip;
484 }
485
486 f.close();
487 return 0;
488}
489
490int vpCalibration::readData(const std::string &filename)
491{
492 vpImagePoint ip;
493 std::ifstream f;
494 f.open(filename.c_str());
495 if (!f.fail()) {
496 unsigned int n;
497 f >> n;
498 std::cout << "There are " << n << " point on the calibration grid " << std::endl;
499
500 clearPoint();
501
502 if (n > 100000)
503 throw(vpException(vpException::badValue, "Bad number of point in the calibration grid"));
504
505 for (unsigned int i = 0; i < n; i++) {
506 double x, y, z, u, v;
507 f >> x >> y >> z >> u >> v;
508 std::cout << x << " " << y << " " << z << " " << u << " " << v << std::endl;
509 ip.set_u(u);
510 ip.set_v(v);
511 addPoint(x, y, z, ip);
512 }
513
514 f.close();
515 return 0;
516 }
517 else {
518 return -1;
519 }
520}
521
522int vpCalibration::readGrid(const std::string &filename, unsigned int &n, std::list<double> &oX, std::list<double> &oY,
523 std::list<double> &oZ, bool verbose)
524{
525 try {
526 std::ifstream f;
527 f.open(filename.c_str());
528 if (!f.fail()) {
529
530 f >> n;
531 if (verbose)
532 std::cout << "There are " << n << " points on the calibration grid " << std::endl;
533 int no_pt;
534 double x, y, z;
535
536 // clear the list
537 oX.clear();
538 oY.clear();
539 oZ.clear();
540
541 if (n > 100000)
542 throw(vpException(vpException::badValue, "Bad number of point in the calibration grid"));
543
544 for (unsigned int i = 0; i < n; i++) {
545 f >> no_pt >> x >> y >> z;
546 if (verbose) {
547 std::cout << no_pt << std::endl;
548 std::cout << x << " " << y << " " << z << std::endl;
549 }
550 oX.push_back(x);
551 oY.push_back(y);
552 oZ.push_back(z);
553 }
554
555 f.close();
556 }
557 else {
558 return -1;
559 }
560 }
561 catch (...) {
562 return -1;
563 }
564 return 0;
565}
566
567int vpCalibration::displayData(vpImage<unsigned char> &I, vpColor color, unsigned int thickness, int subsampling_factor)
568{
569
570 for (std::list<vpImagePoint>::const_iterator it = m_Lip.begin(); it != m_Lip.end(); ++it) {
571 vpImagePoint ip = *it;
572 if (subsampling_factor > 1.) {
573 ip.set_u(ip.get_u() / subsampling_factor);
574 ip.set_v(ip.get_v() / subsampling_factor);
575 }
576 vpDisplay::displayCross(I, ip, 12, color, thickness);
577 }
578 return 0;
579}
580
581int vpCalibration::displayGrid(vpImage<unsigned char> &I, vpColor color, unsigned int thickness, int subsampling_factor)
582{
583 double u0_dist = cam_dist.get_u0() / subsampling_factor;
584 double v0_dist = cam_dist.get_v0() / subsampling_factor;
585 double px_dist = cam_dist.get_px() / subsampling_factor;
586 double py_dist = cam_dist.get_py() / subsampling_factor;
587 double kud_dist = cam_dist.get_kud();
588 // double kdu_dist = cam_dist.get_kdu() ;
589
590 // double u0 = cam.get_u0() ;
591 // double v0 = cam.get_v0() ;
592 // double px = cam.get_px() ;
593 // double py = cam.get_py() ;
594
595 std::list<double>::const_iterator it_LoX = m_LoX.begin();
596 std::list<double>::const_iterator it_LoY = m_LoY.begin();
597 std::list<double>::const_iterator it_LoZ = m_LoZ.begin();
598
599 for (unsigned int i = 0; i < m_npt; i++) {
600 double oX = *it_LoX;
601 double oY = *it_LoY;
602 double oZ = *it_LoZ;
603
604 double cX = oX * cMo_dist[0][0] + oY * cMo_dist[0][1] + oZ * cMo_dist[0][2] + cMo_dist[0][3];
605 double cY = oX * cMo_dist[1][0] + oY * cMo_dist[1][1] + oZ * cMo_dist[1][2] + cMo_dist[1][3];
606 double cZ = oX * cMo_dist[2][0] + oY * cMo_dist[2][1] + oZ * cMo_dist[2][2] + cMo_dist[2][3];
607
608 double x = cX / cZ;
609 double y = cY / cZ;
610
611 double r2 = 1 + kud_dist * (vpMath::sqr(x) + vpMath::sqr(y));
612
613 vpImagePoint ip;
614 ip.set_u(u0_dist + x * px_dist * r2);
615 ip.set_v(v0_dist + y * py_dist * r2);
616
617 vpDisplay::displayCross(I, ip, 6, color, thickness);
619
620 // std::cout << oX << " " << oY << " " <<oZ << std::endl ;
621 // I.getClick() ;
622 ++it_LoX;
623 ++it_LoY;
624 ++it_LoZ;
625 }
626 return 0;
627}
628
629void vpCalibration::setAspectRatio(double aspect_ratio)
630{
631 if (aspect_ratio > 0.) {
632 m_aspect_ratio = aspect_ratio;
633 }
634}
635END_VISP_NAMESPACE
void computeStdDeviation(double &deviation, double &deviation_dist)
int displayData(vpImage< unsigned char > &I, vpColor color=vpColor::red, unsigned int thickness=1, int subsampling_factor=1)
unsigned int get_npt() const
double m_aspect_ratio
int computeCalibration(vpCalibrationMethodType method, vpHomogeneousMatrix &cMo_est, vpCameraParameters &cam_est, bool verbose=false)
int addPoint(double X, double Y, double Z, vpImagePoint &ip)
vpHomogeneousMatrix eMc_dist
vpHomogeneousMatrix rMe
int writeData(const std::string &filename)
vpCalibration & operator=(const vpCalibration &twinCalibration)
vpHomogeneousMatrix cMo
double computeStdDeviation_dist(const vpHomogeneousMatrix &cMo_est, const vpCameraParameters &camera)
static int computeCalibrationMulti(vpCalibrationMethodType method, std::vector< vpCalibration > &table_cal, vpCameraParameters &cam_est, double &globalReprojectionError, bool verbose=false)
vpCameraParameters cam_dist
static int readGrid(const std::string &filename, unsigned int &n, std::list< double > &oX, std::list< double > &oY, std::list< double > &oZ, bool verbose=false)
void setAspectRatio(double aspect_ratio)
vpHomogeneousMatrix eMc
vpHomogeneousMatrix cMo_dist
virtual ~vpCalibration()
int displayGrid(vpImage< unsigned char > &I, vpColor color=vpColor::yellow, unsigned int thickness=1, int subsampling_factor=1)
@ CALIB_LAGRANGE_VIRTUAL_VS_DIST
int readData(const std::string &filename)
vpCameraParameters cam
Generic class defining intrinsic camera parameters.
Class to define RGB colors available for display functionalities.
Definition vpColor.h:157
static void displayCross(const vpImage< unsigned char > &I, const vpImagePoint &ip, unsigned int size, const vpColor &color, unsigned int thickness=1)
error that can be emitted by ViSP classes.
Definition vpException.h:60
@ badValue
Used to indicate that a value is not in the allowed range.
Definition vpException.h:73
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 ...
double get_u() const
void set_u(double u)
void set_v(double v)
double get_v() const
Definition of the vpImage class member functions.
Definition vpImage.h:131
static double sqr(double x)
Definition vpMath.h:203
static void convertPoint(const vpCameraParameters &cam, const double &u, const double &v, double &x, double &y)
Class that defines a 3D point in the object frame and allows forward projection of a 3D point in the ...
Definition vpPoint.h:79
Class used for pose computation from N points (pose from point only). Some of the algorithms implemen...
Definition vpPose.h:82
void addPoint(const vpPoint &P)
Definition vpPose.cpp:96
@ DEMENTHON_LAGRANGE_VIRTUAL_VS
Definition vpPose.h:103
bool computePose(vpPoseMethodType method, vpHomogeneousMatrix &cMo, FuncCheckValidityPose func=nullptr)
Definition vpPose.cpp:385
void clearPoint()
Definition vpPose.cpp:89