Visual Servoing Platform  version 3.6.1 under development (2024-06-12)
vpXmlParserCamera.cpp
1 /****************************************************************************
2  *
3  * ViSP, open source Visual Servoing Platform software.
4  * Copyright (C) 2005 - 2023 by Inria. All rights reserved.
5  *
6  * This software is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * See the file LICENSE.txt at the root directory of this source
11  * distribution for additional information about the GNU GPL.
12  *
13  * For using ViSP with software that can not be combined with the GNU
14  * GPL, please contact Inria about acquiring a ViSP Professional
15  * Edition License.
16  *
17  * See https://visp.inria.fr for more information.
18  *
19  * This software was developed at:
20  * Inria Rennes - Bretagne Atlantique
21  * Campus Universitaire de Beaulieu
22  * 35042 Rennes Cedex
23  * France
24  *
25  * If you have questions regarding the use of this file, please contact
26  * Inria at visp@inria.fr
27  *
28  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
29  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
30  *
31  * Description:
32  * XML parser to load and save camera intrinsic parameters.
33  *
34 *****************************************************************************/
35 
42 #include <visp3/core/vpXmlParserCamera.h>
43 
44 #if defined(VISP_HAVE_PUGIXML)
45 #include <pugixml.hpp>
46 
47 #include <visp3/core/vpDebug.h>
48 /* --------------------------------------------------------------------------
49  */
50  /* --- LABEL XML ------------------------------------------------------------
51  */
52  /* --------------------------------------------------------------------------
53  */
54 
55 #define LABEL_XML_ROOT "root"
56 #define LABEL_XML_CAMERA "camera"
57 #define LABEL_XML_CAMERA_NAME "name"
58 #define LABEL_XML_WIDTH "image_width"
59 #define LABEL_XML_HEIGHT "image_height"
60 #define LABEL_XML_SUBSAMPLING_WIDTH "subsampling_width"
61 #define LABEL_XML_SUBSAMPLING_HEIGHT "subsampling_height"
62 #define LABEL_XML_FULL_WIDTH "full_width"
63 #define LABEL_XML_FULL_HEIGHT "full_height"
64 #define LABEL_XML_MODEL "model"
65 #define LABEL_XML_MODEL_TYPE "type"
66 #define LABEL_XML_U0 "u0"
67 #define LABEL_XML_V0 "v0"
68 #define LABEL_XML_PX "px"
69 #define LABEL_XML_PY "py"
70 #define LABEL_XML_KUD "kud"
71 #define LABEL_XML_KDU "kdu"
72 #define LABEL_XML_K1 "k1"
73 #define LABEL_XML_K2 "k2"
74 #define LABEL_XML_K3 "k3"
75 #define LABEL_XML_K4 "k4"
76 #define LABEL_XML_K5 "k5"
77 
78 #define LABEL_XML_MODEL_WITHOUT_DISTORTION "perspectiveProjWithoutDistortion"
79 #define LABEL_XML_MODEL_WITH_DISTORTION "perspectiveProjWithDistortion"
80 #define LABEL_XML_MODEL_WITH_KANNALA_BRANDT_DISTORTION "ProjWithKannalaBrandtDistortion"
81 
82 #define LABEL_XML_ADDITIONAL_INFO "additional_information"
83 
85 #ifndef DOXYGEN_SHOULD_SKIP_THIS
86 class vpXmlParserCamera::Impl
87 {
88 private:
89  /* --- XML Code------------------------------------------------------------
90  */
91  enum vpXmlCodeType
92  {
93  CODE_XML_BAD = -1,
94  CODE_XML_OTHER,
95  CODE_XML_CAMERA,
96  CODE_XML_CAMERA_NAME,
97  CODE_XML_HEIGHT,
98  CODE_XML_WIDTH,
99  CODE_XML_SUBSAMPLING_WIDTH,
100  CODE_XML_SUBSAMPLING_HEIGHT,
101  CODE_XML_FULL_HEIGHT,
102  CODE_XML_FULL_WIDTH,
103  CODE_XML_MODEL,
104  CODE_XML_MODEL_TYPE,
105  CODE_XML_U0,
106  CODE_XML_V0,
107  CODE_XML_PX,
108  CODE_XML_PY,
109  CODE_XML_KUD,
110  CODE_XML_KDU,
111  CODE_XML_K1,
112  CODE_XML_K2,
113  CODE_XML_K3,
114  CODE_XML_K4,
115  CODE_XML_K5,
116  CODE_XML_ADDITIONAL_INFO
117  };
118 
119 public:
120  Impl()
121  : camera(), camera_name(), image_width(0), image_height(0), subsampling_width(0), subsampling_height(0),
122  full_width(0), full_height(0)
123  { }
124 
125  int parse(vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
126  const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
127  unsigned int im_height, bool verbose)
128  {
129  pugi::xml_document doc;
130  if (!doc.load_file(filename.c_str())) {
131  return SEQUENCE_ERROR;
132  }
133 
134  pugi::xml_node node = doc.document_element();
135  if (!node) {
136  return SEQUENCE_ERROR;
137  }
138 
139  int ret = read(node, cam_name, projModel, im_width, im_height, verbose);
140 
141  cam = camera;
142 
143  return ret;
144  }
145 
163  int read(const pugi::xml_node &node_, const std::string &cam_name,
164  const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
165  unsigned int im_height, bool verbose, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
166  {
167  vpXmlCodeType prop;
169  unsigned int nbCamera = 0;
170 
171  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
172  if (node.type() != pugi::node_element)
173  continue;
174 
175  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
176  prop = CODE_XML_OTHER;
177  back = SEQUENCE_ERROR;
178  }
179  if (prop == CODE_XML_CAMERA) {
180  if (SEQUENCE_OK == read_camera(node, cam_name, projModel, im_width, im_height, subsampl_width, subsampl_height, verbose))
181  nbCamera++;
182  }
183  else
184  back = SEQUENCE_ERROR;
185  }
186 
187  if (nbCamera == 0) {
188  back = SEQUENCE_ERROR;
189  vpCERROR << "No camera parameters is available" << std::endl << "with your specifications" << std::endl;
190  }
191  else if (nbCamera > 1) {
192  back = SEQUENCE_ERROR;
193  vpCERROR << nbCamera << " sets of camera parameters are available" << std::endl
194  << "with your specifications : " << std::endl
195  << "precise your choice..." << std::endl;
196  }
197 
198  return back;
199  }
200 
218  int read_camera(const pugi::xml_node &node_, const std::string &cam_name,
219  const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
220  unsigned int im_height, unsigned int subsampl_width, unsigned int subsampl_height, bool verbose)
221  {
222  vpXmlCodeType prop;
223  /* read value in the XML file. */
224  std::string camera_name_tmp = "";
225  unsigned int image_height_tmp = 0;
226  unsigned int image_width_tmp = 0;
227  unsigned int subsampling_width_tmp = 0;
228  unsigned int subsampling_height_tmp = 0;
229  vpCameraParameters cam_tmp;
230  vpCameraParameters cam_tmp_model;
231  bool same_proj_model = false;
233 
234  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
235  if (node.type() != pugi::node_element)
236  continue;
237 
238  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
239  prop = CODE_XML_OTHER;
240  back = SEQUENCE_ERROR;
241  }
242 
243  switch (prop) {
244  case CODE_XML_CAMERA_NAME: {
245  camera_name_tmp = node.text().as_string();
246  if (verbose) {
247  std::cout << "Found camera with name: \"" << camera_name_tmp << "\"" << std::endl;
248  }
249  break;
250  }
251  case CODE_XML_WIDTH:
252  image_width_tmp = node.text().as_uint();
253  break;
254 
255  case CODE_XML_HEIGHT:
256  image_height_tmp = node.text().as_uint();
257  break;
258  case CODE_XML_SUBSAMPLING_WIDTH:
259  subsampling_width_tmp = node.text().as_uint();
260  break;
261  case CODE_XML_SUBSAMPLING_HEIGHT:
262  subsampling_height_tmp = node.text().as_uint();
263  break;
264 
265  case CODE_XML_MODEL:
266  back = read_camera_model(node, cam_tmp_model);
267  if (cam_tmp_model.get_projModel() == projModel) {
268  cam_tmp = cam_tmp_model;
269  same_proj_model = true; // Same projection model
270  }
271  break;
272 
273  case CODE_XML_ADDITIONAL_INFO:
274  break;
275 
276  case CODE_XML_BAD:
277  case CODE_XML_OTHER:
278  case CODE_XML_CAMERA:
279  case CODE_XML_FULL_HEIGHT:
280  case CODE_XML_FULL_WIDTH:
281  case CODE_XML_MODEL_TYPE:
282  case CODE_XML_U0:
283  case CODE_XML_V0:
284  case CODE_XML_PX:
285  case CODE_XML_PY:
286  case CODE_XML_KUD:
287  case CODE_XML_KDU:
288  case CODE_XML_K1:
289  case CODE_XML_K2:
290  case CODE_XML_K3:
291  case CODE_XML_K4:
292  case CODE_XML_K5:
293  default:
294  back = SEQUENCE_ERROR;
295 
296  break;
297  }
298  }
299  // Create a specific test for subsampling_width and subsampling_height to
300  // ensure that division by zero is not possible in the next test
301  bool test_subsampling_width = true;
302  bool test_subsampling_height = true;
303 
304  if (subsampling_width) {
305  test_subsampling_width = (abs(static_cast<int>(subsampl_width) - static_cast<int>(subsampling_width_tmp)) <
306  (allowedPixelDiffOnImageSize * static_cast<int>(subsampling_width_tmp / subsampling_width)));
307  }
308  if (subsampling_height) {
309  test_subsampling_height = (abs(static_cast<int>(subsampl_height) - static_cast<int>(subsampling_height_tmp)) <
310  (allowedPixelDiffOnImageSize * static_cast<int>(subsampling_height_tmp / subsampling_height)));
311  }
312  // if same name && same projection model && same image size camera already exists, we return SEQUENCE_OK
313  // otherwise it is a new camera that need to be updated and we return SEQUENCE_OK
314  bool same_name = (cam_name.empty() || (cam_name == camera_name_tmp));
315  bool same_img_size = (abs(static_cast<int>(im_width) - static_cast<int>(image_width_tmp)) < allowedPixelDiffOnImageSize || im_width == 0) &&
316  (abs(static_cast<int>(im_height) - static_cast<int>(image_height_tmp)) < allowedPixelDiffOnImageSize || im_height == 0) &&
317  (test_subsampling_width) && (test_subsampling_height);
318  if (same_name && same_img_size && same_proj_model) {
319  back = SEQUENCE_OK; // Camera exists
320  camera = cam_tmp;
321  camera_name = camera_name_tmp;
322  image_width = image_width_tmp;
323  image_height = image_height_tmp;
324  subsampling_width = subsampling_width_tmp;
325  subsampling_height = subsampling_height_tmp;
326  full_width = subsampling_width_tmp * image_width_tmp;
327  full_height = subsampling_height_tmp * image_height_tmp;
328  }
329  else {
330 
331  back = SEQUENCE_ERROR; // Camera doesn't exist yet in the file
332  }
333 #if 0
334  if (!((projModelFound == true) &&
335  (abs(static_cast<int>(im_width) - static_cast<int>(image_width_tmp)) < allowedPixelDiffOnImageSize || im_width == 0) &&
336  (abs(static_cast<int>(im_height) - static_cast<int>(image_height_tmp)) < allowedPixelDiffOnImageSize || im_height == 0) &&
337  (test_subsampling_width) && (test_subsampling_height))) {
338  // Same images size, we need to check if the camera have the same name
339  if (!cam_name.empty() && (cam_name != camera_name_tmp)) {
340  back = SEQUENCE_ERROR; // Camera doesn't exist yet in the file
341  }
342  else {
343  back = SEQUENCE_OK; // Camera already found
344  }
345  }
346  else {
347  camera = cam_tmp;
348  camera_name = camera_name_tmp;
349  image_width = image_width_tmp;
350  image_height = image_height_tmp;
351  subsampling_width = subsampling_width_tmp;
352  subsampling_height = subsampling_height_tmp;
353  full_width = subsampling_width_tmp * image_width_tmp;
354  full_height = subsampling_height_tmp * image_height_tmp;
355  back = SEQUENCE_ERROR; // Camera doesn't exist yet in the file
356  }
357 #endif
358  return back;
359  }
360 
367  vpXmlCodeSequenceType read_camera_model(const pugi::xml_node &node_, vpCameraParameters &cam_tmp)
368  {
369  // counter of the number of read parameters
370  int nb = 0;
371  vpXmlCodeType prop;
372  /* read value in the XML file. */
373 
374  std::string model_type = "";
375  double u0 = cam_tmp.get_u0();
376  double v0 = cam_tmp.get_v0();
377  double px = cam_tmp.get_px();
378  double py = cam_tmp.get_py();
379  double kud = cam_tmp.get_kud();
380  double kdu = cam_tmp.get_kdu();
381  std::vector<double> distortion_coeffs;
383  unsigned int validation = 0;
384 
385  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
386  // vpDEBUG_TRACE (15, "Carac : %s.", node ->name);
387  if (node.type() != pugi::node_element)
388  continue;
389 
390  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
391  prop = CODE_XML_OTHER;
392  back = SEQUENCE_ERROR;
393  }
394 
395  switch (prop) {
396  case CODE_XML_MODEL_TYPE: {
397  model_type = node.text().as_string();
398  nb++;
399  validation = validation | 0x01;
400  } break;
401  case CODE_XML_U0:
402  u0 = node.text().as_double();
403  nb++;
404  validation = validation | 0x02;
405  break;
406  case CODE_XML_V0:
407  v0 = node.text().as_double();
408  nb++;
409  validation = validation | 0x04;
410  break;
411  case CODE_XML_PX:
412  px = node.text().as_double();
413  nb++;
414  validation = validation | 0x08;
415  break;
416  case CODE_XML_PY:
417  py = node.text().as_double();
418  nb++;
419  validation = validation | 0x10;
420  break;
421  case CODE_XML_KUD:
422  kud = node.text().as_double();
423  nb++;
424  validation = validation | 0x20;
425  break;
426  case CODE_XML_KDU:
427  kdu = node.text().as_double();
428  nb++;
429  validation = validation | 0x40;
430  break;
431  case CODE_XML_K1:
432  distortion_coeffs.push_back(node.text().as_double());
433  nb++;
434  validation = validation | 0x20;
435  break;
436  case CODE_XML_K2:
437  distortion_coeffs.push_back(node.text().as_double());
438  nb++;
439  validation = validation | 0x40;
440  break;
441  case CODE_XML_K3:
442  distortion_coeffs.push_back(node.text().as_double());
443  nb++;
444  validation = validation | 0x80;
445  break;
446  case CODE_XML_K4:
447  distortion_coeffs.push_back(node.text().as_double());
448  nb++;
449  validation = validation | 0x100;
450  break;
451  case CODE_XML_K5:
452  distortion_coeffs.push_back(node.text().as_double());
453  nb++;
454  validation = validation | 0x200;
455  break;
456  case CODE_XML_BAD:
457  case CODE_XML_OTHER:
458  case CODE_XML_CAMERA:
459  case CODE_XML_CAMERA_NAME:
460  case CODE_XML_HEIGHT:
461  case CODE_XML_WIDTH:
462  case CODE_XML_SUBSAMPLING_WIDTH:
463  case CODE_XML_SUBSAMPLING_HEIGHT:
464  case CODE_XML_FULL_HEIGHT:
465  case CODE_XML_FULL_WIDTH:
466  case CODE_XML_MODEL:
467  case CODE_XML_ADDITIONAL_INFO:
468  default:
469  back = SEQUENCE_ERROR;
470  break;
471  }
472  }
473 
474  if (model_type.empty()) {
475  vpERROR_TRACE("projection model type doesn't match with any known model !");
476  return SEQUENCE_ERROR;
477  }
478 
479  if (!strcmp(model_type.c_str(), LABEL_XML_MODEL_WITHOUT_DISTORTION)) {
480  if (nb != 5 || validation != 0x001F) {
481  vpCERROR << "ERROR in 'model' field:\n";
482  vpCERROR << "it must contain 5 parameters\n";
483 
484  return SEQUENCE_ERROR;
485  }
486  cam_tmp.initPersProjWithoutDistortion(px, py, u0, v0);
487  }
488  else if (!strcmp(model_type.c_str(), LABEL_XML_MODEL_WITH_DISTORTION)) {
489  if (nb != 7 || validation != 0x7F) {
490  vpCERROR << "ERROR in 'model' field:\n";
491  vpCERROR << "it must contain 7 parameters\n";
492 
493  return SEQUENCE_ERROR;
494  }
495  cam_tmp.initPersProjWithDistortion(px, py, u0, v0, kud, kdu);
496  }
497  else if (!strcmp(model_type.c_str(), LABEL_XML_MODEL_WITH_KANNALA_BRANDT_DISTORTION)) {
498  if (nb != 10 || validation != 0x3FF) { // at least one coefficient is missing. We should know which one
499  vpCERROR << "ERROR in 'model' field:\n";
500  vpCERROR << "it must contain 10 parameters\n";
501 
502  std::vector<double> fixed_distortion_coeffs;
503 
504  // In case distortion coefficients are missing, we should complete them with 0 values
505  // Since 0x3FF is 0011|1111|1111 and we are interested in the most significant 1s shown below
506  // -- ---
507  // If we divide by 32 (>> 2^5 : 5 remaining least significant bits), we will have to check 5 bits only
508  int check = validation / 32;
509  int j = 0;
510 
511  for (int i = 0; i < 5; ++i) {
512  int bit = check % 2; // if bit == 1 => the corresponding distortion coefficient is present.
513  if (!bit)
514  fixed_distortion_coeffs.push_back(0.);
515  else
516  fixed_distortion_coeffs.push_back(distortion_coeffs[j++]);
517  check /= 2;
518  }
519 
520  cam_tmp.initProjWithKannalaBrandtDistortion(px, py, u0, v0, fixed_distortion_coeffs);
521  return SEQUENCE_ERROR;
522  }
523  cam_tmp.initProjWithKannalaBrandtDistortion(px, py, u0, v0, distortion_coeffs);
524  }
525  else {
526  vpERROR_TRACE("projection model type doesn't match with any known model !");
527 
528  return SEQUENCE_ERROR;
529  }
530  return back;
531  }
532 
533  int save(const vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
534  unsigned int im_width, unsigned int im_height, const std::string &additionalInfo, bool verbose)
535  {
536  pugi::xml_document doc;
537  pugi::xml_node node;
538 
539  if (!doc.load_file(filename.c_str(), pugi::parse_default | pugi::parse_comments)) {
540  node = doc.append_child(pugi::node_declaration);
541  node.append_attribute("version") = "1.0";
542  node = doc.append_child(LABEL_XML_ROOT);
543  pugi::xml_node nodeComment = node.append_child(pugi::node_comment);
544  nodeComment.set_value("This file stores intrinsic camera parameters used\n"
545  " in the vpCameraParameters Class of ViSP available\n"
546  " at https://visp.inria.fr/download/ .\n"
547  " It can be read with the parse method of\n"
548  " the vpXmlParserCamera class.");
549  }
550 
551  node = doc.document_element();
552  if (!node) {
553  return SEQUENCE_ERROR;
554  }
555 
556  camera = cam;
557 
558  int nbCamera = count(node, cam_name, cam.get_projModel(), im_width, im_height, verbose);
559  if (nbCamera) {
560  return SEQUENCE_ERROR;
561  }
562 
563  pugi::xml_node nodeCamera = find_camera(node, cam_name, im_width, im_height);
564  if (!nodeCamera) {
565  write(node, cam_name, im_width, im_height);
566  }
567  else {
568  write_camera(nodeCamera);
569  }
570 
571  if (!additionalInfo.empty()) {
572  // Get camera node pointer
573  nodeCamera = find_camera(node, cam_name, im_width, im_height);
574 
575  // Additional information provided by the user
576  pugi::xml_node nodeAdditionalInfo = find_additional_info(nodeCamera);
577 
578  if (!nodeAdditionalInfo) {
579  // Create the additional information node
580  pugi::xml_node node_comment = nodeCamera.append_child(pugi::node_comment);
581  node_comment.set_value("Additional information");
582 
583  nodeAdditionalInfo = nodeCamera.append_child(LABEL_XML_ADDITIONAL_INFO);
584  }
585 
586  if (nodeAdditionalInfo) {
587  // Add the information in this specific node
588  pugi::xml_document tmpDoc;
589  if (tmpDoc.load_string(additionalInfo.c_str())) {
590  for (node = tmpDoc.first_child(); node; node = node.next_sibling()) {
591  nodeAdditionalInfo.append_copy(node);
592  }
593  }
594  }
595  }
596 
597  doc.save_file(filename.c_str(), PUGIXML_TEXT(" "));
598 
599  return SEQUENCE_OK;
600  }
601 
620  int count(const pugi::xml_node &node_, const std::string &cam_name,
621  const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
622  unsigned int im_height, bool verbose, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
623  {
624  vpXmlCodeType prop;
625  int nbCamera = 0;
626 
627  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
628  if (node.type() != pugi::node_element)
629  continue;
630 
631  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
632  prop = CODE_XML_OTHER;
633  }
634 
635  if (prop == CODE_XML_CAMERA) {
636  if (SEQUENCE_OK == read_camera(node, cam_name, projModel, im_width, im_height, subsampl_width, subsampl_height, verbose))
637  nbCamera++;
638  }
639  }
640 
641  return nbCamera;
642  }
643 
660  pugi::xml_node find_camera(const pugi::xml_node &node_, const std::string &cam_name, unsigned int im_width,
661  unsigned int im_height, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
662  {
663  vpXmlCodeType prop;
664  pugi::xml_node resNode = pugi::xml_node();
665 
666  pugi::xml_node node = node_.first_child();
667  bool hasNotFoundCam = true;
668  while (node && hasNotFoundCam) {
669  if (node.type() == pugi::node_element) {
670  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
671  prop = CODE_XML_OTHER;
672  }
673  if (prop == CODE_XML_CAMERA) {
674  if (SEQUENCE_OK == read_camera_header(node, cam_name, im_width, im_height, subsampl_width, subsampl_height)) {
675  resNode = node;
676  hasNotFoundCam = false;
677  }
678  }
679  }
680  node = node.next_sibling();
681  }
682  return resNode;
683  }
684 
700  int read_camera_header(const pugi::xml_node &node_, const std::string &cam_name, unsigned int im_width,
701  unsigned int im_height, unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
702  {
703  vpXmlCodeType prop;
704  /* read value in the XML file. */
705  std::string camera_name_tmp = "";
706  unsigned int image_height_tmp = 0;
707  unsigned int image_width_tmp = 0;
708  unsigned int subsampling_width_tmp = 0;
709  unsigned int subsampling_height_tmp = 0;
711 
712  for (pugi::xml_node node = node_.first_child(); node; node = node.next_sibling()) {
713  if (node.type() != pugi::node_element)
714  continue;
715  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
716  prop = CODE_XML_OTHER;
717  back = SEQUENCE_ERROR;
718  }
719 
720  switch (prop) {
721  case CODE_XML_CAMERA_NAME:
722  camera_name_tmp = node.text().as_string();
723  break;
724 
725  case CODE_XML_WIDTH:
726  image_width_tmp = node.text().as_uint();
727  break;
728 
729  case CODE_XML_HEIGHT:
730  image_height_tmp = node.text().as_uint();
731  break;
732 
733  case CODE_XML_SUBSAMPLING_WIDTH:
734  subsampling_width_tmp = node.text().as_uint();
735  break;
736 
737  case CODE_XML_SUBSAMPLING_HEIGHT:
738  subsampling_height_tmp = node.text().as_uint();
739  break;
740 
741  case CODE_XML_MODEL:
742  break;
743 
744  case CODE_XML_ADDITIONAL_INFO:
745  break;
746 
747  case CODE_XML_BAD:
748  case CODE_XML_OTHER:
749  case CODE_XML_CAMERA:
750  case CODE_XML_FULL_HEIGHT:
751  case CODE_XML_FULL_WIDTH:
752  case CODE_XML_MODEL_TYPE:
753  case CODE_XML_U0:
754  case CODE_XML_V0:
755  case CODE_XML_PX:
756  case CODE_XML_PY:
757  case CODE_XML_KUD:
758  case CODE_XML_KDU:
759  default:
760  back = SEQUENCE_ERROR;
761  break;
762  }
763  }
764  if (!((cam_name == camera_name_tmp) && (im_width == image_width_tmp || im_width == 0) &&
765  (im_height == image_height_tmp || im_height == 0) &&
766  (subsampl_width == subsampling_width_tmp || subsampl_width == 0) &&
767  (subsampl_height == subsampling_height_tmp || subsampl_height == 0))) {
768  back = SEQUENCE_ERROR;
769  }
770  return back;
771  }
772 
788  int write(pugi::xml_node &node, const std::string &cam_name, unsigned int im_width, unsigned int im_height,
789  unsigned int subsampl_width = 0, unsigned int subsampl_height = 0)
790  {
791  int back = SEQUENCE_OK;
792 
793  // <camera>
794  pugi::xml_node node_camera = node.append_child(LABEL_XML_CAMERA);
795 
796  pugi::xml_node node_tmp;
797  {
798  //<name>
799  if (!cam_name.empty()) {
800  node_tmp = node_camera.append_child(pugi::node_comment);
801  node_tmp.set_value("Name of the camera");
802  node_tmp = node_camera.append_child(LABEL_XML_CAMERA_NAME);
803  node_tmp.append_child(pugi::node_pcdata).set_value(cam_name.c_str());
804  }
805 
806  if (im_width != 0 || im_height != 0) {
807  node_tmp = node_camera.append_child(pugi::node_comment);
808  node_tmp.set_value("Size of the image on which camera "
809  "calibration was performed");
810 
811  //<image_width>
812  node_tmp = node_camera.append_child(LABEL_XML_WIDTH);
813  node_tmp.append_child(pugi::node_pcdata).text() = im_width;
814 
815  //<image_height>
816  node_tmp = node_camera.append_child(LABEL_XML_HEIGHT);
817  node_tmp.append_child(pugi::node_pcdata).text() = im_height;
818  if (subsampling_width != 0 || subsampling_height != 0) {
819  node_tmp = node_camera.append_child(pugi::node_comment);
820  node_tmp.set_value("Subsampling used to obtain the "
821  "current size of the image.");
822 
823  //<subsampling_width>
824  node_tmp = node_camera.append_child(LABEL_XML_SUBSAMPLING_WIDTH);
825  node_tmp.append_child(pugi::node_pcdata).text() = subsampl_width;
826  //<subsampling_height>
827  node_tmp = node_camera.append_child(LABEL_XML_SUBSAMPLING_HEIGHT);
828  node_tmp.append_child(pugi::node_pcdata).text() = subsampl_height;
829  node_tmp = node_camera.append_child(pugi::node_comment);
830  node_tmp.set_value("The full size is the sensor size actually used to "
831  "grab the image. full_width = subsampling_width * "
832  "image_width");
833 
834  //<full_width>
835  node_tmp = node_camera.append_child(LABEL_XML_FULL_WIDTH);
836  node_tmp.append_child(pugi::node_pcdata).text() = im_width * subsampl_width;
837  //<full_height>
838  node_tmp = node_camera.append_child(LABEL_XML_FULL_HEIGHT);
839  node_tmp.append_child(pugi::node_pcdata).text() = im_height * subsampl_height;
840  }
841  }
842 
843  node_tmp = node_camera.append_child(pugi::node_comment);
844  node_tmp.set_value("Intrinsic camera parameters "
845  "computed for each projection model");
846 
847  back = write_camera(node_camera);
848  }
849  return back;
850  }
851 
857  int write_camera(pugi::xml_node &node_camera)
858  {
859  pugi::xml_node node_model;
860  pugi::xml_node node_tmp;
861 
862  int back = SEQUENCE_OK;
863 
864  switch (camera.get_projModel()) {
866  //<model>
867  node_model = node_camera.append_child(LABEL_XML_MODEL);
868  {
869  node_tmp = node_model.append_child(pugi::node_comment);
870  node_tmp.set_value("Projection model type");
871 
872  //<type>without_distortion</type>
873  node_tmp = node_model.append_child(LABEL_XML_MODEL_TYPE);
874  node_tmp.append_child(pugi::node_pcdata).set_value(LABEL_XML_MODEL_WITHOUT_DISTORTION);
875 
876  node_tmp = node_model.append_child(pugi::node_comment);
877  node_tmp.set_value("Pixel ratio");
878  //<px>
879  node_tmp = node_model.append_child(LABEL_XML_PX);
880  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_px();
881  //<py>
882  node_tmp = node_model.append_child(LABEL_XML_PY);
883  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_py();
884 
885  node_tmp = node_model.append_child(pugi::node_comment);
886  node_tmp.set_value("Principal point");
887 
888  //<u0>
889  node_tmp = node_model.append_child(LABEL_XML_U0);
890  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_u0();
891  //<v0>
892  node_tmp = node_model.append_child(LABEL_XML_V0);
893  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_v0();
894  }
895  break;
896 
898  //<model>
899  node_model = node_camera.append_child(LABEL_XML_MODEL);
900  {
901  node_tmp = node_model.append_child(pugi::node_comment);
902  node_tmp.set_value("Projection model type");
903  //<type>with_distortion</type>
904  node_tmp = node_model.append_child(LABEL_XML_MODEL_TYPE);
905  node_tmp.append_child(pugi::node_pcdata).set_value(LABEL_XML_MODEL_WITH_DISTORTION);
906 
907  node_tmp = node_model.append_child(pugi::node_comment);
908  node_tmp.set_value("Pixel ratio");
909  //<px>
910  node_tmp = node_model.append_child(LABEL_XML_PX);
911  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_px();
912  //<py>
913  node_tmp = node_model.append_child(LABEL_XML_PY);
914  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_py();
915 
916  node_tmp = node_model.append_child(pugi::node_comment);
917  node_tmp.set_value("Principal point");
918  //<u0>
919  node_tmp = node_model.append_child(LABEL_XML_U0);
920  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_u0();
921  //<v0>
922  node_tmp = node_model.append_child(LABEL_XML_V0);
923  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_v0();
924 
925  //<kud>
926  node_tmp = node_model.append_child(pugi::node_comment);
927  node_tmp.set_value("Undistorted to distorted distortion parameter");
928  node_tmp = node_model.append_child(LABEL_XML_KUD);
929  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_kud();
930 
931  //<kud>
932  node_tmp = node_model.append_child(pugi::node_comment);
933  node_tmp.set_value("Distorted to undistorted distortion parameter");
934  node_tmp = node_model.append_child(LABEL_XML_KDU);
935  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_kdu();
936  }
937  break;
938 
940  //<model>
941  node_model = node_camera.append_child(LABEL_XML_MODEL);
942  {
943  node_tmp = node_model.append_child(pugi::node_comment);
944  node_tmp.set_value("Projection model type");
945  //<type>with_KannalaBrandt_distortion</type>
946  node_tmp = node_model.append_child(LABEL_XML_MODEL_TYPE);
947  node_tmp.append_child(pugi::node_pcdata).set_value(LABEL_XML_MODEL_WITH_KANNALA_BRANDT_DISTORTION);
948 
949  node_tmp = node_model.append_child(pugi::node_comment);
950  node_tmp.set_value("Pixel ratio");
951  //<px>
952  node_tmp = node_model.append_child(LABEL_XML_PX);
953  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_px();
954  //<py>
955  node_tmp = node_model.append_child(LABEL_XML_PY);
956  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_py();
957 
958  node_tmp = node_model.append_child(pugi::node_comment);
959  node_tmp.set_value("Principal point");
960  //<u0>
961  node_tmp = node_model.append_child(LABEL_XML_U0);
962  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_u0();
963  //<v0>
964  node_tmp = node_model.append_child(LABEL_XML_V0);
965  node_tmp.append_child(pugi::node_pcdata).text() = camera.get_v0();
966 
967  //<k1>, <k2>, <k3>, <k4>, <k5>
968  std::vector<double> distortion_coefs = camera.getKannalaBrandtDistortionCoefficients();
969 
970  if (distortion_coefs.size() != 5)
971  std::cout << "Make sure to have 5 distortion coefficients for Kannala-Brandt distortions." << std::endl;
972 
973  node_tmp = node_model.append_child(pugi::node_comment);
974  node_tmp.set_value("Distortion coefficients");
975  node_tmp = node_model.append_child(LABEL_XML_K1);
976  distortion_coefs.size() == 0 ? node_tmp.append_child(pugi::node_pcdata).text() = 0
977  : node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[0];
978  node_tmp = node_model.append_child(LABEL_XML_K2);
979  distortion_coefs.size() <= 1 ? node_tmp.append_child(pugi::node_pcdata).text() = 0
980  : node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[1];
981  node_tmp = node_model.append_child(LABEL_XML_K3);
982  distortion_coefs.size() <= 2 ? node_tmp.append_child(pugi::node_pcdata).text() = 0
983  : node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[2];
984  node_tmp = node_model.append_child(LABEL_XML_K4);
985  distortion_coefs.size() <= 3 ? node_tmp.append_child(pugi::node_pcdata).text() = 0
986  : node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[3];
987  node_tmp = node_model.append_child(LABEL_XML_K5);
988  distortion_coefs.size() <= 4 ? node_tmp.append_child(pugi::node_pcdata).text() = 0
989  : node_tmp.append_child(pugi::node_pcdata).text() = distortion_coefs[4];
990  }
991  break;
992  }
993  return back;
994  }
995 
1002  pugi::xml_node find_additional_info(const pugi::xml_node &node_)
1003  {
1004  vpXmlCodeType prop;
1005  pugi::xml_node resNode = pugi::xml_node();
1006 
1007  pugi::xml_node node = node_.first_child();
1008  bool hasNotFoundInfo = true;
1009  while (node && hasNotFoundInfo) {
1010  if (node.type() == pugi::node_element) {
1011  if (SEQUENCE_OK != str2xmlcode(node.name(), prop)) {
1012  prop = CODE_XML_OTHER;
1013  }
1014 
1015  if (prop == CODE_XML_ADDITIONAL_INFO) {
1016  // We found the node
1017  resNode = node;
1018  hasNotFoundInfo = false;
1019  }
1020  }
1021 
1022  node = node.next_sibling();
1023  }
1024 
1025  return resNode;
1026  }
1027 
1034  vpXmlCodeSequenceType str2xmlcode(const char *str, vpXmlCodeType &res)
1035  {
1036  vpXmlCodeType val_int = CODE_XML_BAD;
1038 
1039  if (!strcmp(str, LABEL_XML_CAMERA)) {
1040  val_int = CODE_XML_CAMERA;
1041  }
1042  else if (!strcmp(str, LABEL_XML_CAMERA_NAME)) {
1043  val_int = CODE_XML_CAMERA_NAME;
1044  }
1045  else if (!strcmp(str, LABEL_XML_MODEL)) {
1046  val_int = CODE_XML_MODEL;
1047  }
1048  else if (!strcmp(str, LABEL_XML_MODEL_TYPE)) {
1049  val_int = CODE_XML_MODEL_TYPE;
1050  }
1051  else if (!strcmp(str, LABEL_XML_WIDTH)) {
1052  val_int = CODE_XML_WIDTH;
1053  }
1054  else if (!strcmp(str, LABEL_XML_HEIGHT)) {
1055  val_int = CODE_XML_HEIGHT;
1056  }
1057  else if (!strcmp(str, LABEL_XML_SUBSAMPLING_WIDTH)) {
1058  val_int = CODE_XML_SUBSAMPLING_WIDTH;
1059  }
1060  else if (!strcmp(str, LABEL_XML_SUBSAMPLING_HEIGHT)) {
1061  val_int = CODE_XML_SUBSAMPLING_HEIGHT;
1062  }
1063  else if (!strcmp(str, LABEL_XML_FULL_WIDTH)) {
1064  val_int = CODE_XML_FULL_WIDTH;
1065  }
1066  else if (!strcmp(str, LABEL_XML_FULL_HEIGHT)) {
1067  val_int = CODE_XML_FULL_HEIGHT;
1068  }
1069  else if (!strcmp(str, LABEL_XML_U0)) {
1070  val_int = CODE_XML_U0;
1071  }
1072  else if (!strcmp(str, LABEL_XML_V0)) {
1073  val_int = CODE_XML_V0;
1074  }
1075  else if (!strcmp(str, LABEL_XML_PX)) {
1076  val_int = CODE_XML_PX;
1077  }
1078  else if (!strcmp(str, LABEL_XML_PY)) {
1079  val_int = CODE_XML_PY;
1080  }
1081  else if (!strcmp(str, LABEL_XML_KUD)) {
1082  val_int = CODE_XML_KUD;
1083  }
1084  else if (!strcmp(str, LABEL_XML_KDU)) {
1085  val_int = CODE_XML_KDU;
1086  }
1087  else if (!strcmp(str, LABEL_XML_K1)) {
1088  val_int = CODE_XML_K1;
1089  }
1090  else if (!strcmp(str, LABEL_XML_K2)) {
1091  val_int = CODE_XML_K2;
1092  }
1093  else if (!strcmp(str, LABEL_XML_K3)) {
1094  val_int = CODE_XML_K3;
1095  }
1096  else if (!strcmp(str, LABEL_XML_K4)) {
1097  val_int = CODE_XML_K4;
1098  }
1099  else if (!strcmp(str, LABEL_XML_K5)) {
1100  val_int = CODE_XML_K5;
1101  }
1102  else if (!strcmp(str, LABEL_XML_ADDITIONAL_INFO)) {
1103  val_int = CODE_XML_ADDITIONAL_INFO;
1104  }
1105  else {
1106  val_int = CODE_XML_OTHER;
1107  }
1108  res = val_int;
1109 
1110  return back;
1111  }
1112 
1113  std::string getCameraName() const { return camera_name; }
1114  vpCameraParameters getCameraParameters() const { return camera; }
1115  unsigned int getHeight() const { return image_height; }
1116  unsigned int getSubsampling_width() const { return subsampling_width; }
1117  unsigned int getSubsampling_height() const { return subsampling_height; }
1118  unsigned int getWidth() const { return image_width; }
1119 
1120  void setCameraName(const std::string &name) { camera_name = name; }
1121  void setHeight(unsigned int height) { image_height = height; }
1122  void setSubsampling_width(unsigned int subsampling) { subsampling_width = subsampling; }
1123  void setSubsampling_height(unsigned int subsampling) { subsampling_height = subsampling; }
1124  void setWidth(unsigned int width) { image_width = width; }
1125 
1126 private:
1127  vpCameraParameters camera;
1128  std::string camera_name;
1129  unsigned int image_width;
1130  unsigned int image_height;
1131  unsigned int subsampling_width;
1132  unsigned int subsampling_height;
1133  unsigned int full_width;
1134  unsigned int full_height;
1135 
1138  static const int allowedPixelDiffOnImageSize = 15;
1139 };
1140 #endif // DOXYGEN_SHOULD_SKIP_THIS
1141 
1142 vpXmlParserCamera::vpXmlParserCamera() : m_impl(new Impl()) { }
1143 
1145 
1161 int vpXmlParserCamera::parse(vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
1162  const vpCameraParameters::vpCameraParametersProjType &projModel, unsigned int im_width,
1163  unsigned int im_height, bool verbose)
1164 {
1165  return m_impl->parse(cam, filename, cam_name, projModel, im_width, im_height, verbose);
1166 }
1167 
1212 int vpXmlParserCamera::save(const vpCameraParameters &cam, const std::string &filename, const std::string &cam_name,
1213  unsigned int im_width, unsigned int im_height, const std::string &additionalInfo, bool verbose)
1214 {
1215  return m_impl->save(cam, filename, cam_name, im_width, im_height, additionalInfo, verbose);
1216 }
1217 
1218 std::string vpXmlParserCamera::getCameraName() const { return m_impl->getCameraName(); }
1219 
1220 vpCameraParameters vpXmlParserCamera::getCameraParameters() const { return m_impl->getCameraParameters(); }
1221 
1222 unsigned int vpXmlParserCamera::getHeight() const { return m_impl->getHeight(); }
1223 
1224 unsigned int vpXmlParserCamera::getSubsampling_width() const { return m_impl->getSubsampling_width(); }
1225 
1226 unsigned int vpXmlParserCamera::getSubsampling_height() const { return m_impl->getSubsampling_height(); }
1227 
1228 unsigned int vpXmlParserCamera::getWidth() const { return m_impl->getWidth(); }
1229 
1230 void vpXmlParserCamera::setCameraName(const std::string &name) { m_impl->setCameraName(name); }
1231 
1232 void vpXmlParserCamera::setHeight(unsigned int height) { m_impl->setHeight(height); }
1233 
1234 void vpXmlParserCamera::setSubsampling_width(unsigned int subsampling) { m_impl->setSubsampling_width(subsampling); }
1235 
1236 void vpXmlParserCamera::setSubsampling_height(unsigned int subsampling) { m_impl->setSubsampling_height(subsampling); }
1237 
1238 void vpXmlParserCamera::setWidth(unsigned int width) { m_impl->setWidth(width); }
1240 #elif !defined(VISP_BUILD_SHARED_LIBS)
1241 // Work around to avoid warning: libvisp_core.a(vpXmlParserCamera.cpp.o) has no symbols
1242 void dummy_vpXmlParserCamera() { };
1243 
1244 #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)
double get_kdu() const
vpCameraParametersProjType get_projModel() const
void initProjWithKannalaBrandtDistortion(double px, double py, double u0, double v0, const std::vector< double > &distortion_coefficients)
double get_kud() const
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
#define vpCERROR
Definition: vpDebug.h:359
#define vpERROR_TRACE
Definition: vpDebug.h:385