Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
vpXmlParserCamera.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 * XML parser to load and save camera intrinsic parameters.
32 */
33
39
40#include <visp3/core/vpXmlParserCamera.h>
41
42#if defined(VISP_HAVE_PUGIXML)
43#include <pugixml.hpp>
44
45/* --------------------------------------------------------------------------
46 */
47 /* --- LABEL XML ------------------------------------------------------------
48 */
49 /* --------------------------------------------------------------------------
50 */
51
52#define LABEL_XML_ROOT "root"
53#define LABEL_XML_CAMERA "camera"
54#define LABEL_XML_CAMERA_NAME "name"
55#define LABEL_XML_WIDTH "image_width"
56#define LABEL_XML_HEIGHT "image_height"
57#define LABEL_XML_SUBSAMPLING_WIDTH "subsampling_width"
58#define LABEL_XML_SUBSAMPLING_HEIGHT "subsampling_height"
59#define LABEL_XML_FULL_WIDTH "full_width"
60#define LABEL_XML_FULL_HEIGHT "full_height"
61#define LABEL_XML_MODEL "model"
62#define LABEL_XML_MODEL_TYPE "type"
63#define LABEL_XML_U0 "u0"
64#define LABEL_XML_V0 "v0"
65#define LABEL_XML_PX "px"
66#define LABEL_XML_PY "py"
67#define LABEL_XML_KUD "kud"
68#define LABEL_XML_KDU "kdu"
69#define LABEL_XML_K1 "k1"
70#define LABEL_XML_K2 "k2"
71#define LABEL_XML_K3 "k3"
72#define LABEL_XML_K4 "k4"
73#define LABEL_XML_K5 "k5"
74
75#define LABEL_XML_MODEL_WITHOUT_DISTORTION "perspectiveProjWithoutDistortion"
76#define LABEL_XML_MODEL_WITH_DISTORTION "perspectiveProjWithDistortion"
77#define LABEL_XML_MODEL_WITH_KANNALA_BRANDT_DISTORTION "ProjWithKannalaBrandtDistortion"
78
79#define LABEL_XML_ADDITIONAL_INFO "additional_information"
80
82#ifndef DOXYGEN_SHOULD_SKIP_THIS
83class vpXmlParserCamera::Impl
84{
85public: /* --- XML Code------------------------------------------------------------
86 */
87 enum vpXmlCodeType
88 {
89 CODE_XML_BAD = -1,
90 CODE_XML_OTHER,
91 CODE_XML_CAMERA,
92 CODE_XML_CAMERA_NAME,
93 CODE_XML_HEIGHT,
94 CODE_XML_WIDTH,
95 CODE_XML_SUBSAMPLING_WIDTH,
96 CODE_XML_SUBSAMPLING_HEIGHT,
97 CODE_XML_FULL_HEIGHT,
98 CODE_XML_FULL_WIDTH,
99 CODE_XML_MODEL,
100 CODE_XML_MODEL_TYPE,
101 CODE_XML_U0,
102 CODE_XML_V0,
103 CODE_XML_PX,
104 CODE_XML_PY,
105 CODE_XML_KUD,
106 CODE_XML_KDU,
107 CODE_XML_K1,
108 CODE_XML_K2,
109 CODE_XML_K3,
110 CODE_XML_K4,
111 CODE_XML_K5,
112 CODE_XML_ADDITIONAL_INFO
113 };
114
115 Impl()
116 : camera(), camera_name(), image_width(0), image_height(0), subsampling_width(0), subsampling_height(0),
117 full_width(0), full_height(0)
118 { }
119
120 int parse(vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
121 const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
122 unsigned int im_height, bool verbose)
123 {
124 pugi::xml_document doc;
125 if (!doc.load_file(filename.c_str())) {
126 return SEQUENCE_ERROR;
127 }
128
129 pugi::xml_node node = doc.document_element();
130 if (!node) {
131 return SEQUENCE_ERROR;
132 }
133
134 int ret = read(node, cam_name, projModel, im_width, im_height, verbose);
135
136 cam = camera;
137
138 return ret;
139 }
140
158 int read(const pugi::xml_node &node_, const std::string &cam_name,
159 const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
160 unsigned int im_height, bool verbose, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
161 {
162 vpXmlCodeType prop;
163 vpXmlCodeSequenceType back = SEQUENCE_OK;
164 unsigned int nbCamera = 0;
165
166 for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
167 if (node.type() == pugi::node_element) {
168 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
169 prop = CODE_XML_OTHER;
170 back = SEQUENCE_ERROR;
171 }
172 if (prop == CODE_XML_CAMERA) {
173 if (SEQUENCE_OK == read_camera(node, cam_name, projModel, im_width, im_height, subsampl_width, subsampl_height, verbose)) {
174 ++nbCamera;
175 }
176 }
177 else {
178 back = SEQUENCE_ERROR;
179 }
180 }
181 }
182
183 if (nbCamera == 0) {
184 back = SEQUENCE_ERROR;
185 std::cout << "Warning: No camera parameters is available" << std::endl << "with your specifications" << std::endl;
186 }
187 else if (nbCamera > 1) {
188 back = SEQUENCE_ERROR;
189 std::cout << "Warning: " << nbCamera << " sets of camera parameters are available" << std::endl
190 << "with your specifications : " << std::endl
191 << "precise your choice..." << std::endl;
192 }
193
194 return back;
195 }
196
214 int read_camera(const pugi::xml_node &node_, const std::string &cam_name,
215 const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
216 unsigned int im_height, unsigned int subsampl_width, unsigned int subsampl_height, bool verbose)
217 {
218 vpXmlCodeType prop;
219 /* read value in the XML file. */
220 std::string camera_name_tmp = "";
221 unsigned int image_height_tmp = 0;
222 unsigned int image_width_tmp = 0;
223 unsigned int subsampling_width_tmp = 0;
224 unsigned int subsampling_height_tmp = 0;
225 vpCameraParameters cam_tmp;
226 vpCameraParameters cam_tmp_model;
227 bool same_proj_model = false;
228 vpXmlCodeSequenceType back = SEQUENCE_OK;
229
230 for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
231 if (node.type() == pugi::node_element) {
232 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
233 prop = CODE_XML_OTHER;
234 back = SEQUENCE_ERROR;
235 }
236
237 switch (prop) {
238 case CODE_XML_CAMERA_NAME: {
239 camera_name_tmp = node.text().as_string();
240 if (verbose) {
241 std::cout << "Found camera with name: \"" << camera_name_tmp << "\"" << std::endl;
242 }
243 break;
244 }
245 case CODE_XML_WIDTH:
246 image_width_tmp = node.text().as_uint();
247 break;
248
249 case CODE_XML_HEIGHT:
250 image_height_tmp = node.text().as_uint();
251 break;
252 case CODE_XML_SUBSAMPLING_WIDTH:
253 subsampling_width_tmp = node.text().as_uint();
254 break;
255 case CODE_XML_SUBSAMPLING_HEIGHT:
256 subsampling_height_tmp = node.text().as_uint();
257 break;
258
259 case CODE_XML_MODEL:
260 back = read_camera_model(node, cam_tmp_model);
261 if (cam_tmp_model.get_projModel() == projModel) {
262 cam_tmp = cam_tmp_model;
263 same_proj_model = true; // Same projection model
264 }
265 break;
266
267 case CODE_XML_ADDITIONAL_INFO:
268 break;
269
270 case CODE_XML_BAD:
271 case CODE_XML_OTHER:
272 case CODE_XML_CAMERA:
273 case CODE_XML_FULL_HEIGHT:
274 case CODE_XML_FULL_WIDTH:
275 case CODE_XML_MODEL_TYPE:
276 case CODE_XML_U0:
277 case CODE_XML_V0:
278 case CODE_XML_PX:
279 case CODE_XML_PY:
280 case CODE_XML_KUD:
281 case CODE_XML_KDU:
282 case CODE_XML_K1:
283 case CODE_XML_K2:
284 case CODE_XML_K3:
285 case CODE_XML_K4:
286 case CODE_XML_K5:
287 default:
288 back = SEQUENCE_ERROR;
289 break;
290 }
291 }
292 }
293 // Create a specific test for subsampling_width and subsampling_height to
294 // ensure that division by zero is not possible in the next test
295 bool test_subsampling_width = true;
296 bool test_subsampling_height = true;
297
298 if (subsampling_width) {
299 test_subsampling_width = (abs(static_cast<int>(subsampl_width) - static_cast<int>(subsampling_width_tmp)) <
300 (allowedPixelDiffOnImageSize * static_cast<int>(subsampling_width_tmp / subsampling_width)));
301 }
302 if (subsampling_height) {
303 test_subsampling_height = (abs(static_cast<int>(subsampl_height) - static_cast<int>(subsampling_height_tmp)) <
304 (allowedPixelDiffOnImageSize * static_cast<int>(subsampling_height_tmp / subsampling_height)));
305 }
306 // if same name && same projection model && same image size camera already exists, we return SEQUENCE_OK
307 // otherwise it is a new camera that need to be updated and we return SEQUENCE_OK
308 bool same_name = (cam_name.empty() || (cam_name == camera_name_tmp));
309 bool imWidthOk = (abs(static_cast<int>(im_width) - static_cast<int>(image_width_tmp)) < allowedPixelDiffOnImageSize) || (im_width == 0);
310 bool imHeightOk = (abs(static_cast<int>(im_height) - static_cast<int>(image_height_tmp)) < allowedPixelDiffOnImageSize) || (im_height == 0);
311 bool imSizeOk = imWidthOk && imHeightOk;
312 bool same_img_size = imSizeOk && test_subsampling_width && test_subsampling_height;
313 if (same_name && same_img_size && same_proj_model) {
314 back = SEQUENCE_OK; // Camera exists
315 camera = cam_tmp;
316 camera_name = camera_name_tmp;
317 image_width = image_width_tmp;
318 image_height = image_height_tmp;
319 subsampling_width = subsampling_width_tmp;
320 subsampling_height = subsampling_height_tmp;
321 full_width = subsampling_width_tmp * image_width_tmp;
322 full_height = subsampling_height_tmp * image_height_tmp;
323 }
324 else {
325
326 back = SEQUENCE_ERROR; // Camera doesn't exist yet in the file
327 }
328#if 0
329 if (!((projModelFound == true) &&
330 (abs(static_cast<int>(im_width) - static_cast<int>(image_width_tmp)) < allowedPixelDiffOnImageSize || im_width == 0) &&
331 (abs(static_cast<int>(im_height) - static_cast<int>(image_height_tmp)) < allowedPixelDiffOnImageSize || im_height == 0) &&
332 (test_subsampling_width) && (test_subsampling_height))) {
333 // Same images size, we need to check if the camera have the same name
334 if (!cam_name.empty() && (cam_name != camera_name_tmp)) {
335 back = SEQUENCE_ERROR; // Camera doesn't exist yet in the file
336 }
337 else {
338 back = SEQUENCE_OK; // Camera already found
339 }
340 }
341 else {
342 camera = cam_tmp;
343 camera_name = camera_name_tmp;
344 image_width = image_width_tmp;
345 image_height = image_height_tmp;
346 subsampling_width = subsampling_width_tmp;
347 subsampling_height = subsampling_height_tmp;
348 full_width = subsampling_width_tmp * image_width_tmp;
349 full_height = subsampling_height_tmp * image_height_tmp;
350 back = SEQUENCE_ERROR; // Camera doesn't exist yet in the file
351 }
352#endif
353 return back;
354 }
355
362 vpXmlCodeSequenceType read_camera_model(const pugi::xml_node &node_, vpCameraParameters &cam_tmp)
363 {
364 // counter of the number of read parameters
365 int nb = 0;
366 vpXmlCodeType prop;
367 /* read value in the XML file. */
368
369 std::string model_type = "";
370 double u0 = cam_tmp.get_u0();
371 double v0 = cam_tmp.get_v0();
372 double px = cam_tmp.get_px();
373 double py = cam_tmp.get_py();
374 double kud = cam_tmp.get_kud();
375 double kdu = cam_tmp.get_kdu();
376 std::vector<double> distortion_coeffs;
377 vpXmlCodeSequenceType back = SEQUENCE_OK;
378 unsigned int validation = 0;
379
380 for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
381 if (node.type() == pugi::node_element) {
382 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
383 prop = CODE_XML_OTHER;
384 back = SEQUENCE_ERROR;
385 }
386
387 switch (prop) {
388 case CODE_XML_MODEL_TYPE: {
389 model_type = node.text().as_string();
390 ++nb;
391 validation = validation | 0x01;
392 } break;
393 case CODE_XML_U0:
394 u0 = node.text().as_double();
395 ++nb;
396 validation = validation | 0x02;
397 break;
398 case CODE_XML_V0:
399 v0 = node.text().as_double();
400 ++nb;
401 validation = validation | 0x04;
402 break;
403 case CODE_XML_PX:
404 px = node.text().as_double();
405 ++nb;
406 validation = validation | 0x08;
407 break;
408 case CODE_XML_PY:
409 py = node.text().as_double();
410 ++nb;
411 validation = validation | 0x10;
412 break;
413 case CODE_XML_KUD:
414 kud = node.text().as_double();
415 ++nb;
416 validation = validation | 0x20;
417 break;
418 case CODE_XML_KDU:
419 kdu = node.text().as_double();
420 ++nb;
421 validation = validation | 0x40;
422 break;
423 case CODE_XML_K1:
424 distortion_coeffs.push_back(node.text().as_double());
425 ++nb;
426 validation = validation | 0x20;
427 break;
428 case CODE_XML_K2:
429 distortion_coeffs.push_back(node.text().as_double());
430 ++nb;
431 validation = validation | 0x40;
432 break;
433 case CODE_XML_K3:
434 distortion_coeffs.push_back(node.text().as_double());
435 ++nb;
436 validation = validation | 0x80;
437 break;
438 case CODE_XML_K4:
439 distortion_coeffs.push_back(node.text().as_double());
440 ++nb;
441 validation = validation | 0x100;
442 break;
443 case CODE_XML_K5:
444 distortion_coeffs.push_back(node.text().as_double());
445 ++nb;
446 validation = validation | 0x200;
447 break;
448 case CODE_XML_BAD:
449 case CODE_XML_OTHER:
450 case CODE_XML_CAMERA:
451 case CODE_XML_CAMERA_NAME:
452 case CODE_XML_HEIGHT:
453 case CODE_XML_WIDTH:
454 case CODE_XML_SUBSAMPLING_WIDTH:
455 case CODE_XML_SUBSAMPLING_HEIGHT:
456 case CODE_XML_FULL_HEIGHT:
457 case CODE_XML_FULL_WIDTH:
458 case CODE_XML_MODEL:
459 case CODE_XML_ADDITIONAL_INFO:
460 default:
461 back = SEQUENCE_ERROR;
462 break;
463 }
464 }
465 }
466
467 if (model_type.empty()) {
468 std::cout << "Warning: projection model type doesn't match with any known model !" << std::endl;
469 return SEQUENCE_ERROR;
470 }
471
472 if (!strcmp(model_type.c_str(), LABEL_XML_MODEL_WITHOUT_DISTORTION)) {
473 if ((nb != 5) || (validation != 0x001F)) {
474 std::cout << "ERROR in 'model' field:\n";
475 std::cout << "it must contain 5 parameters\n";
476
477 return SEQUENCE_ERROR;
478 }
479 cam_tmp.initPersProjWithoutDistortion(px, py, u0, v0);
480 }
481 else if (!strcmp(model_type.c_str(), LABEL_XML_MODEL_WITH_DISTORTION)) {
482 if ((nb != 7) || (validation != 0x7F)) {
483 std::cout << "ERROR in 'model' field:\n";
484 std::cout << "it must contain 7 parameters\n";
485
486 return SEQUENCE_ERROR;
487 }
488 cam_tmp.initPersProjWithDistortion(px, py, u0, v0, kud, kdu);
489 }
490 else if (!strcmp(model_type.c_str(), LABEL_XML_MODEL_WITH_KANNALA_BRANDT_DISTORTION)) {
491 if ((nb != 10) || (validation != 0x3FF)) { // at least one coefficient is missing. We should know which one
492 std::cout << "ERROR in 'model' field:\n";
493 std::cout << "it must contain 10 parameters\n";
494
495 std::vector<double> fixed_distortion_coeffs;
496
497 // In case distortion coefficients are missing, we should complete them with 0 values
498 // Since 0x3FF is 0011|1111|1111 and we are interested in the most significant 1s shown below
499 // -- ---
500 // If we divide by 32 (>> 2^5 : 5 remaining least significant bits), we will have to check 5 bits only
501 const int dividerForBitCheck = 32;
502 int check = validation / dividerForBitCheck;
503 unsigned int j = 0;
504
505 const int nbRemainingBits = 5;
506 const int moduloForOddity = 2;
507 const int dividerForRightShift = 2;
508 for (size_t i = 0; i < nbRemainingBits; ++i) {
509 int bit = check % moduloForOddity; // if bit == 1 => the corresponding distortion coefficient is present.
510 if (!bit) {
511 fixed_distortion_coeffs.push_back(0.);
512 }
513 else {
514 fixed_distortion_coeffs.push_back(distortion_coeffs[j++]);
515 }
516 check /= dividerForRightShift;
517 }
518
519 cam_tmp.initProjWithKannalaBrandtDistortion(px, py, u0, v0, fixed_distortion_coeffs);
520 return SEQUENCE_ERROR;
521 }
522 cam_tmp.initProjWithKannalaBrandtDistortion(px, py, u0, v0, distortion_coeffs);
523 }
524 else {
525 std::cout << "Warning: projection model type doesn't match with any known model !" << std::endl;
526
527 return SEQUENCE_ERROR;
528 }
529 return back;
530 }
531
532 int save(const vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
533 unsigned int im_width, unsigned int im_height, const std::string &additionalInfo, bool verbose)
534 {
535 pugi::xml_document doc;
536 pugi::xml_node node;
537
538 if (!doc.load_file(filename.c_str(), pugi::parse_default | pugi::parse_comments)) {
539 node = doc.append_child(pugi::node_declaration);
540 node.append_attribute("version") = "1.0";
541 node = doc.append_child(LABEL_XML_ROOT);
542 pugi::xml_node nodeComment = node.append_child(pugi::node_comment);
543 nodeComment.set_value("This file stores intrinsic camera parameters used\n"
544 " in the vpCameraParameters Class of ViSP available\n"
545 " at https://visp.inria.fr/download/ .\n"
546 " It can be read with the parse method of\n"
547 " the vpXmlParserCamera class.");
548 }
549
550 node = doc.document_element();
551 if (!node) {
552 return SEQUENCE_ERROR;
553 }
554
555 camera = cam;
556
557 int nbCamera = count(node, cam_name, cam.get_projModel(), im_width, im_height, verbose);
558 if (nbCamera) {
559 return SEQUENCE_ERROR;
560 }
561
562 pugi::xml_node nodeCamera = find_camera(node, cam_name, im_width, im_height);
563 if (!nodeCamera) {
564 write(node, cam_name, im_width, im_height);
565 }
566 else {
567 write_camera(nodeCamera);
568 }
569
570 if (!additionalInfo.empty()) {
571 // Get camera node pointer
572 nodeCamera = find_camera(node, cam_name, im_width, im_height);
573
574 // Additional information provided by the user
575 pugi::xml_node nodeAdditionalInfo = find_additional_info(nodeCamera);
576
577 if (!nodeAdditionalInfo) {
578 // Create the additional information node
579 pugi::xml_node node_comment = nodeCamera.append_child(pugi::node_comment);
580 node_comment.set_value("Additional information");
581
582 nodeAdditionalInfo = nodeCamera.append_child(LABEL_XML_ADDITIONAL_INFO);
583 }
584
585 if (nodeAdditionalInfo) {
586 // Add the information in this specific node
587 pugi::xml_document tmpDoc;
588 if (tmpDoc.load_string(additionalInfo.c_str())) {
589 for (node = tmpDoc.first_child(); node; node = node.next_sibling()) {
590 nodeAdditionalInfo.append_copy(node);
591 }
592 }
593 }
594 }
595
596 doc.save_file(filename.c_str(), PUGIXML_TEXT(" "));
597
598 return SEQUENCE_OK;
599 }
600
619 int count(const pugi::xml_node &node_, const std::string &cam_name,
620 const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
621 unsigned int im_height, bool verbose, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
622 {
623 vpXmlCodeType prop;
624 int nbCamera = 0;
625
626 for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
627 if (node.type() == pugi::node_element) {
628 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
629 prop = CODE_XML_OTHER;
630 }
631
632 if (prop == CODE_XML_CAMERA) {
633 if (SEQUENCE_OK == read_camera(node, cam_name, projModel, im_width, im_height, subsampl_width, subsampl_height, verbose)) {
634 ++nbCamera;
635 }
636 }
637 }
638 }
639
640 return nbCamera;
641 }
642
659 pugi::xml_node find_camera(const pugi::xml_node &node_, const std::string &cam_name, unsigned int im_width,
660 unsigned int im_height, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
661 {
662 vpXmlCodeType prop;
663 pugi::xml_node resNode = pugi::xml_node();
664
665 pugi::xml_node node = node_.first_child();
666 bool hasNotFoundCam = true;
667 while (node && hasNotFoundCam) {
668 if (node.type() == pugi::node_element) {
669 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
670 prop = CODE_XML_OTHER;
671 }
672 if (prop == CODE_XML_CAMERA) {
673 if (SEQUENCE_OK == read_camera_header(node, cam_name, im_width, im_height, subsampl_width, subsampl_height)) {
674 resNode = node;
675 hasNotFoundCam = false;
676 }
677 }
678 }
679 node = node.next_sibling();
680 }
681 return resNode;
682 }
683
699 int read_camera_header(const pugi::xml_node &node_, const std::string &cam_name, unsigned int im_width,
700 unsigned int im_height, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
701 {
702 vpXmlCodeType prop;
703 /* read value in the XML file. */
704 std::string camera_name_tmp = "";
705 unsigned int image_height_tmp = 0;
706 unsigned int image_width_tmp = 0;
707 unsigned int subsampling_width_tmp = 0;
708 unsigned int subsampling_height_tmp = 0;
709 vpXmlCodeSequenceType back = SEQUENCE_OK;
710
711 for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
712 if (node.type() == pugi::node_element) {
713 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
714 prop = CODE_XML_OTHER;
715 back = SEQUENCE_ERROR;
716 }
717
718 switch (prop) {
719 case CODE_XML_CAMERA_NAME:
720 camera_name_tmp = node.text().as_string();
721 break;
722
723 case CODE_XML_WIDTH:
724 image_width_tmp = node.text().as_uint();
725 break;
726
727 case CODE_XML_HEIGHT:
728 image_height_tmp = node.text().as_uint();
729 break;
730
731 case CODE_XML_SUBSAMPLING_WIDTH:
732 subsampling_width_tmp = node.text().as_uint();
733 break;
734
735 case CODE_XML_SUBSAMPLING_HEIGHT:
736 subsampling_height_tmp = node.text().as_uint();
737 break;
738
739 case CODE_XML_ADDITIONAL_INFO:
740 case CODE_XML_BAD:
741 case CODE_XML_OTHER:
742 case CODE_XML_CAMERA:
743 case CODE_XML_FULL_HEIGHT:
744 case CODE_XML_FULL_WIDTH:
745 case CODE_XML_MODEL:
746 case CODE_XML_MODEL_TYPE:
747 case CODE_XML_U0:
748 case CODE_XML_V0:
749 case CODE_XML_PX:
750 case CODE_XML_PY:
751 case CODE_XML_KUD:
752 case CODE_XML_KDU:
753 case CODE_XML_K1:
754 case CODE_XML_K2:
755 case CODE_XML_K3:
756 case CODE_XML_K4:
757 case CODE_XML_K5:
758 default:
759 back = SEQUENCE_ERROR;
760 break;
761 }
762 }
763 }
764 bool imHeightOK = (im_height == image_height_tmp) || (im_height == 0);
765 bool imWidthOK = (im_width == image_width_tmp) || (im_width == 0);
766 bool imSizeEqual = imHeightOK && imWidthOK;
767 bool subsampleHeightOK = (subsampl_height == subsampling_height_tmp) || (subsampl_height == 0);
768 bool subsampleWidthOK = (subsampl_width == subsampling_width_tmp) || (subsampl_width == 0);
769 bool subsampleOK = subsampleHeightOK && subsampleWidthOK;
770 if (!((cam_name == camera_name_tmp) && imSizeEqual && subsampleOK)) {
771 back = SEQUENCE_ERROR;
772 }
773 return back;
774 }
775
791 int write(pugi::xml_node &node, const std::string &cam_name, unsigned int im_width, unsigned int im_height,
792 unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
793 {
794 int back = SEQUENCE_OK;
795
796 // <camera>
797 pugi::xml_node node_camera = node.append_child(LABEL_XML_CAMERA);
798
799 pugi::xml_node node_tmp;
800 {
801 // <name>
802 if (!cam_name.empty()) {
803 node_tmp = node_camera.append_child(pugi::node_comment);
804 node_tmp.set_value("Name of the camera");
805 node_tmp = node_camera.append_child(LABEL_XML_CAMERA_NAME);
806 node_tmp.append_child(pugi::node_pcdata).set_value(cam_name.c_str());
807 }
808
809 if ((im_width != 0) || (im_height != 0)) {
810 node_tmp = node_camera.append_child(pugi::node_comment);
811 node_tmp.set_value("Size of the image on which camera "
812 "calibration was performed");
813
814 // <image_width>
815 node_tmp = node_camera.append_child(LABEL_XML_WIDTH);
816 node_tmp.append_child(pugi::node_pcdata).text() = im_width;
817
818 // <image_height>
819 node_tmp = node_camera.append_child(LABEL_XML_HEIGHT);
820 node_tmp.append_child(pugi::node_pcdata).text() = im_height;
821 if ((subsampling_width != 0) || (subsampling_height != 0)) {
822 node_tmp = node_camera.append_child(pugi::node_comment);
823 node_tmp.set_value("Subsampling used to obtain the "
824 "current size of the image.");
825
826 // <subsampling_width>
827 node_tmp = node_camera.append_child(LABEL_XML_SUBSAMPLING_WIDTH);
828 node_tmp.append_child(pugi::node_pcdata).text() = subsampl_width;
829 // <subsampling_height>
830 node_tmp = node_camera.append_child(LABEL_XML_SUBSAMPLING_HEIGHT);
831 node_tmp.append_child(pugi::node_pcdata).text() = subsampl_height;
832 node_tmp = node_camera.append_child(pugi::node_comment);
833 node_tmp.set_value("The full size is the sensor size actually used to "
834 "grab the image. full_width = subsampling_width * "
835 "image_width");
836
837 // <full_width>
838 node_tmp = node_camera.append_child(LABEL_XML_FULL_WIDTH);
839 node_tmp.append_child(pugi::node_pcdata).text() = im_width * subsampl_width;
840 // <full_height>
841 node_tmp = node_camera.append_child(LABEL_XML_FULL_HEIGHT);
842 node_tmp.append_child(pugi::node_pcdata).text() = im_height * subsampl_height;
843 }
844 }
845
846 node_tmp = node_camera.append_child(pugi::node_comment);
847 node_tmp.set_value("Intrinsic camera parameters "
848 "computed for each projection model");
849
850 back = write_camera(node_camera);
851 }
852 return back;
853 }
854
860 int write_camera(pugi::xml_node &node_camera)
861 {
862 pugi::xml_node node_model;
863 pugi::xml_node node_tmp;
864
865 int back = SEQUENCE_OK;
866
867 switch (camera.get_projModel()) {
869 writeCameraWithoutDistortion(node_camera);
870 break;
871
873 writeCameraWithDistortion(node_camera);
874 break;
875
877 writeCameraWithKannalaBrandt(node_camera);
878 break;
879 default: {
880 throw(vpException(vpException::fatalError, "Unsupported camera projection model in vpXmlParserCamera::write_camera()"));
881 }
882 }
883 return back;
884 }
885
892 pugi::xml_node find_additional_info(const pugi::xml_node &node_)
893 {
894 vpXmlCodeType prop;
895 pugi::xml_node resNode = pugi::xml_node();
896
897 pugi::xml_node node = node_.first_child();
898 bool hasNotFoundInfo = true;
899 while (node && hasNotFoundInfo) {
900 if (node.type() == pugi::node_element) {
901 if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
902 prop = CODE_XML_OTHER;
903 }
904
905 if (prop == CODE_XML_ADDITIONAL_INFO) {
906 // We found the node
907 resNode = node;
908 hasNotFoundInfo = false;
909 }
910 }
911
912 node = node.next_sibling();
913 }
914
915 return resNode;
916 }
917
924 vpXmlCodeSequenceType str2xmlcode(const char *str, vpXmlCodeType &res)
925 {
926 vpXmlCodeType val_int = CODE_XML_BAD;
927 vpXmlCodeSequenceType back = vpXmlParserCamera::SEQUENCE_OK;
928
929 if (!strcmp(str, LABEL_XML_CAMERA)) {
930 val_int = CODE_XML_CAMERA;
931 }
932 else if (!strcmp(str, LABEL_XML_CAMERA_NAME)) {
933 val_int = CODE_XML_CAMERA_NAME;
934 }
935 else if (!strcmp(str, LABEL_XML_MODEL)) {
936 val_int = CODE_XML_MODEL;
937 }
938 else if (!strcmp(str, LABEL_XML_MODEL_TYPE)) {
939 val_int = CODE_XML_MODEL_TYPE;
940 }
941 else if (!strcmp(str, LABEL_XML_WIDTH)) {
942 val_int = CODE_XML_WIDTH;
943 }
944 else if (!strcmp(str, LABEL_XML_HEIGHT)) {
945 val_int = CODE_XML_HEIGHT;
946 }
947 else if (!strcmp(str, LABEL_XML_SUBSAMPLING_WIDTH)) {
948 val_int = CODE_XML_SUBSAMPLING_WIDTH;
949 }
950 else if (!strcmp(str, LABEL_XML_SUBSAMPLING_HEIGHT)) {
951 val_int = CODE_XML_SUBSAMPLING_HEIGHT;
952 }
953 else if (!strcmp(str, LABEL_XML_FULL_WIDTH)) {
954 val_int = CODE_XML_FULL_WIDTH;
955 }
956 else if (!strcmp(str, LABEL_XML_FULL_HEIGHT)) {
957 val_int = CODE_XML_FULL_HEIGHT;
958 }
959 else if (!strcmp(str, LABEL_XML_U0)) {
960 val_int = CODE_XML_U0;
961 }
962 else if (!strcmp(str, LABEL_XML_V0)) {
963 val_int = CODE_XML_V0;
964 }
965 else if (!strcmp(str, LABEL_XML_PX)) {
966 val_int = CODE_XML_PX;
967 }
968 else if (!strcmp(str, LABEL_XML_PY)) {
969 val_int = CODE_XML_PY;
970 }
971 else if (!strcmp(str, LABEL_XML_KUD)) {
972 val_int = CODE_XML_KUD;
973 }
974 else if (!strcmp(str, LABEL_XML_KDU)) {
975 val_int = CODE_XML_KDU;
976 }
977 else if (!strcmp(str, LABEL_XML_K1)) {
978 val_int = CODE_XML_K1;
979 }
980 else if (!strcmp(str, LABEL_XML_K2)) {
981 val_int = CODE_XML_K2;
982 }
983 else if (!strcmp(str, LABEL_XML_K3)) {
984 val_int = CODE_XML_K3;
985 }
986 else if (!strcmp(str, LABEL_XML_K4)) {
987 val_int = CODE_XML_K4;
988 }
989 else if (!strcmp(str, LABEL_XML_K5)) {
990 val_int = CODE_XML_K5;
991 }
992 else if (!strcmp(str, LABEL_XML_ADDITIONAL_INFO)) {
993 val_int = CODE_XML_ADDITIONAL_INFO;
994 }
995 else {
996 val_int = CODE_XML_OTHER;
997 }
998 res = val_int;
999
1000 return back;
1001 }
1002
1003 std::string getCameraName() const { return camera_name; }
1004 vpCameraParameters getCameraParameters() const { return camera; }
1005 unsigned int getHeight() const { return image_height; }
1006 unsigned int getSubsampling_width() const { return subsampling_width; }
1007 unsigned int getSubsampling_height() const { return subsampling_height; }
1008 unsigned int getWidth() const { return image_width; }
1009
1010 void setCameraName(const std::string &name) { camera_name = name; }
1011 void setHeight(unsigned int height) { image_height = height; }
1012 void setSubsampling_width(unsigned int subsampling) { subsampling_width = subsampling; }
1013 void setSubsampling_height(unsigned int subsampling) { subsampling_height = subsampling; }
1014 void setWidth(unsigned int width) { image_width = width; }
1015
1016private:
1021 void writeCameraWithoutDistortion(pugi::xml_node &node_camera)
1022 {
1023 pugi::xml_node node_model;
1024 pugi::xml_node node_tmp;
1025 // <model>
1026 node_model = node_camera.append_child(LABEL_XML_MODEL);
1027 node_tmp = node_model.append_child(pugi::node_comment);
1028 node_tmp.set_value("Projection model type");
1029
1030 // <type>without_distortion</type>
1031 node_tmp = node_model.append_child(LABEL_XML_MODEL_TYPE);
1032 node_tmp.append_child(pugi::node_pcdata).set_value(LABEL_XML_MODEL_WITHOUT_DISTORTION);
1033
1034 node_tmp = node_model.append_child(pugi::node_comment);
1035 node_tmp.set_value("Pixel ratio");
1036 // <px>
1037 node_tmp = node_model.append_child(LABEL_XML_PX);
1038 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_px();
1039 // <py>
1040 node_tmp = node_model.append_child(LABEL_XML_PY);
1041 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_py();
1042
1043 node_tmp = node_model.append_child(pugi::node_comment);
1044 node_tmp.set_value("Principal point");
1045
1046 // <u0>
1047 node_tmp = node_model.append_child(LABEL_XML_U0);
1048 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_u0();
1049 // <v0>
1050 node_tmp = node_model.append_child(LABEL_XML_V0);
1051 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_v0();
1052 }
1053
1058 void writeCameraWithDistortion(pugi::xml_node &node_camera)
1059 {
1060 pugi::xml_node node_model;
1061 pugi::xml_node node_tmp;
1062 // <model>
1063 node_model = node_camera.append_child(LABEL_XML_MODEL);
1064 node_tmp = node_model.append_child(pugi::node_comment);
1065 node_tmp.set_value("Projection model type");
1066 // <type>with_distortion</type>
1067 node_tmp = node_model.append_child(LABEL_XML_MODEL_TYPE);
1068 node_tmp.append_child(pugi::node_pcdata).set_value(LABEL_XML_MODEL_WITH_DISTORTION);
1069
1070 node_tmp = node_model.append_child(pugi::node_comment);
1071 node_tmp.set_value("Pixel ratio");
1072 // <px>
1073 node_tmp = node_model.append_child(LABEL_XML_PX);
1074 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_px();
1075 // <py>
1076 node_tmp = node_model.append_child(LABEL_XML_PY);
1077 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_py();
1078
1079 node_tmp = node_model.append_child(pugi::node_comment);
1080 node_tmp.set_value("Principal point");
1081 // <u0>
1082 node_tmp = node_model.append_child(LABEL_XML_U0);
1083 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_u0();
1084 // <v0>
1085 node_tmp = node_model.append_child(LABEL_XML_V0);
1086 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_v0();
1087
1088 // <kud>
1089 node_tmp = node_model.append_child(pugi::node_comment);
1090 node_tmp.set_value("Undistorted to distorted distortion parameter");
1091 node_tmp = node_model.append_child(LABEL_XML_KUD);
1092 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_kud();
1093
1094 // <kud>
1095 node_tmp = node_model.append_child(pugi::node_comment);
1096 node_tmp.set_value("Distorted to undistorted distortion parameter");
1097 node_tmp = node_model.append_child(LABEL_XML_KDU);
1098 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_kdu();
1099 }
1100
1105 void writeCameraWithKannalaBrandt(pugi::xml_node &node_camera)
1106 {
1107 const unsigned int index_0 = 0;
1108 const unsigned int index_1 = 1;
1109 const unsigned int index_2 = 2;
1110 const unsigned int index_3 = 3;
1111 const unsigned int index_4 = 4;
1112 const unsigned int requiredNbCoeff = 5;
1113 pugi::xml_node node_model;
1114 pugi::xml_node node_tmp;
1115 // <model>
1116 node_model = node_camera.append_child(LABEL_XML_MODEL);
1117 node_tmp = node_model.append_child(pugi::node_comment);
1118 node_tmp.set_value("Projection model type");
1119 // <type>with_KannalaBrandt_distortion</type>
1120 node_tmp = node_model.append_child(LABEL_XML_MODEL_TYPE);
1121 node_tmp.append_child(pugi::node_pcdata).set_value(LABEL_XML_MODEL_WITH_KANNALA_BRANDT_DISTORTION);
1122
1123 node_tmp = node_model.append_child(pugi::node_comment);
1124 node_tmp.set_value("Pixel ratio");
1125 // <px>
1126 node_tmp = node_model.append_child(LABEL_XML_PX);
1127 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_px();
1128 // <py>
1129 node_tmp = node_model.append_child(LABEL_XML_PY);
1130 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_py();
1131
1132 node_tmp = node_model.append_child(pugi::node_comment);
1133 node_tmp.set_value("Principal point");
1134 // <u0>
1135 node_tmp = node_model.append_child(LABEL_XML_U0);
1136 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_u0();
1137 // <v0>
1138 node_tmp = node_model.append_child(LABEL_XML_V0);
1139 node_tmp.append_child(pugi::node_pcdata).text() = camera.get_v0();
1140
1141 std::vector<double> distortion_coefs = camera.getKannalaBrandtDistortionCoefficients();
1142
1143 if (distortion_coefs.size() != requiredNbCoeff) {
1144 std::cout << "Make sure to have 5 distortion coefficients for Kannala-Brandt distortions." << std::endl;
1145 }
1146
1147 node_tmp = node_model.append_child(pugi::node_comment);
1148 node_tmp.set_value("Distortion coefficients");
1149 node_tmp = node_model.append_child(LABEL_XML_K1);
1150 distortion_coefs.size() == index_0 ? (node_tmp.append_child(pugi::node_pcdata).text() = 0)
1151 : (node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[index_0]);
1152 node_tmp = node_model.append_child(LABEL_XML_K2);
1153 distortion_coefs.size() <= index_1 ? (node_tmp.append_child(pugi::node_pcdata).text() = 0)
1154 : (node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[index_1]);
1155 node_tmp = node_model.append_child(LABEL_XML_K3);
1156 distortion_coefs.size() <= index_2 ? (node_tmp.append_child(pugi::node_pcdata).text() = 0)
1157 : (node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[index_2]);
1158 node_tmp = node_model.append_child(LABEL_XML_K4);
1159 distortion_coefs.size() <= index_3 ? (node_tmp.append_child(pugi::node_pcdata).text() = 0)
1160 : (node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[index_3]);
1161 node_tmp = node_model.append_child(LABEL_XML_K5);
1162 distortion_coefs.size() <= index_4 ? (node_tmp.append_child(pugi::node_pcdata).text() = 0)
1163 : (node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[index_4]);
1164 }
1165
1166 vpCameraParameters camera;
1167 std::string camera_name;
1168 unsigned int image_width;
1169 unsigned int image_height;
1170 unsigned int subsampling_width;
1171 unsigned int subsampling_height;
1172 unsigned int full_width;
1173 unsigned int full_height;
1174
1177 static const int allowedPixelDiffOnImageSize = 15;
1178};
1179#endif // DOXYGEN_SHOULD_SKIP_THIS
1180
1182
1184
1200int vpXmlParserCamera::parse(vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
1201 const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
1202 unsigned int im_height, bool verbose)
1203{
1204 return m_impl->parse(cam, filename, cam_name, projModel, im_width, im_height, verbose);
1205}
1206
1255int vpXmlParserCamera::save(const vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
1256 unsigned int im_width, unsigned int im_height, const std::string &additionalInfo, bool verbose)
1257{
1258 return m_impl->save(cam, filename, cam_name, im_width, im_height, additionalInfo, verbose);
1259}
1260
1261std::string vpXmlParserCamera::getCameraName() const { return m_impl->getCameraName(); }
1262
1263vpCameraParameters vpXmlParserCamera::getCameraParameters() const { return m_impl->getCameraParameters(); }
1264
1265unsigned int vpXmlParserCamera::getHeight() const { return m_impl->getHeight(); }
1266
1267unsigned int vpXmlParserCamera::getSubsampling_width() const { return m_impl->getSubsampling_width(); }
1268
1269unsigned int vpXmlParserCamera::getSubsampling_height() const { return m_impl->getSubsampling_height(); }
1270
1271unsigned int vpXmlParserCamera::getWidth() const { return m_impl->getWidth(); }
1272
1273void vpXmlParserCamera::setCameraName(const std::string &name) { m_impl->setCameraName(name); }
1274
1275void vpXmlParserCamera::setHeight(unsigned int height) { m_impl->setHeight(height); }
1276
1277void vpXmlParserCamera::setSubsampling_width(unsigned int subsampling) { m_impl->setSubsampling_width(subsampling); }
1278
1279void vpXmlParserCamera::setSubsampling_height(unsigned int subsampling) { m_impl->setSubsampling_height(subsampling); }
1280
1281void vpXmlParserCamera::setWidth(unsigned int width) { m_impl->setWidth(width); }
1282END_VISP_NAMESPACE
1283#elif !defined(VISP_BUILD_SHARED_LIBS)
1284// Work around to avoid warning: libvisp_core.a(vpXmlParserCamera.cpp.o) has no symbols
1285void dummy_vpXmlParserCamera() { }
1286
1287#endif
Generic class defining intrinsic camera parameters.
void initPersProjWithoutDistortion(double px, double py, double u0, double v0)
@ perspectiveProjWithDistortion
Perspective projection with distortion model.
@ ProjWithKannalaBrandtDistortion
Projection with Kannala-Brandt distortion model.
@ perspectiveProjWithoutDistortion
Perspective projection without distortion model.
void initPersProjWithDistortion(double px, double py, double u0, double v0, double kud, double kdu)
vpCameraParametersProjType get_projModel() const
void initProjWithKannalaBrandtDistortion(double px, double py, double u0, double v0, const std::vector< double > &distortion_coefficients)
@ fatalError
Fatal error.
Definition vpException.h:72
void setSubsampling_width(unsigned int subsampling)
void setWidth(unsigned int width)
unsigned int getHeight() const
int save(const vpCameraParameters &cam, const std::string &filename, const std::string &camera_name, unsigned int image_width=0, unsigned int image_height=0, const std::string &additionalInfo="", bool verbose=true)
vpCameraParameters getCameraParameters() const
unsigned int getWidth() const
void setSubsampling_height(unsigned int subsampling)
void setCameraName(const std::string &name)
void setHeight(unsigned int height)
unsigned int getSubsampling_height() const
unsigned int getSubsampling_width() const
int parse(vpCameraParameters &cam, const std::string &filename, const std::string &camera_name, const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int image_width=0, unsigned int image_height=0, bool verbose=true)
std::string getCameraName() const