PageRenderTime 60ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/pointmatcher/IO.cpp

https://github.com/ndavid/libpointmatcher
C++ | 2224 lines | 1686 code | 308 blank | 230 comment | 539 complexity | e72347857a0abe8a19239c56249d5203 MD5 | raw file
Possible License(s): BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. // kate: replace-tabs off; indent-width 4; indent-mode normal
  2. // vim: ts=4:sw=4:noexpandtab
  3. /*
  4. Copyright (c) 2010--2012,
  5. Franรงois Pomerleau and Stephane Magnenat, ASL, ETHZ, Switzerland
  6. You can contact the authors at <f dot pomerleau at gmail dot com> and
  7. <stephane at magnenat dot net>
  8. All rights reserved.
  9. Redistribution and use in source and binary forms, with or without
  10. modification, are permitted provided that the following conditions are met:
  11. * Redistributions of source code must retain the above copyright
  12. notice, this list of conditions and the following disclaimer.
  13. * Redistributions in binary form must reproduce the above copyright
  14. notice, this list of conditions and the following disclaimer in the
  15. documentation and/or other materials provided with the distribution.
  16. * Neither the name of the <organization> nor the
  17. names of its contributors may be used to endorse or promote products
  18. derived from this software without specific prior written permission.
  19. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  20. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. DISCLAIMED. IN NO EVENT SHALL ETH-ASL BE LIABLE FOR ANY
  23. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  26. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. */
  30. #include "IO.h"
  31. #include "InspectorsImpl.h"
  32. // For logging
  33. #include "PointMatcherPrivate.h"
  34. #include <iostream>
  35. #include <fstream>
  36. #include <stdexcept>
  37. #include <ctype.h>
  38. #include "boost/algorithm/string.hpp"
  39. #include "boost/filesystem.hpp"
  40. #include "boost/filesystem/path.hpp"
  41. #include "boost/filesystem/operations.hpp"
  42. #include "boost/lexical_cast.hpp"
  43. #include "boost/foreach.hpp"
  44. #ifdef WIN32
  45. #define strtok_r strtok_s
  46. #endif // WIN32
  47. using namespace std;
  48. using namespace PointMatcherSupport;
  49. // Tokenize a string, excepted if it begins with a '%' (a comment in CSV)
  50. static std::vector<string> csvLineToVector(const char* line)
  51. {
  52. std::vector<string> parsedLine;
  53. char delimiters[] = " \t,;";
  54. char *token;
  55. char tmpLine[1024];
  56. char *brkt = 0;
  57. strcpy(tmpLine, line);
  58. token = strtok_r(tmpLine, delimiters, &brkt);
  59. if(line[0] != '%') // Jump line if it's commented
  60. {
  61. while (token)
  62. {
  63. parsedLine.push_back(string(token));
  64. token = strtok_r(NULL, delimiters, &brkt);
  65. }
  66. }
  67. return parsedLine;
  68. }
  69. // Open and parse a CSV file, return the data
  70. CsvElements parseCsvWithHeader(const std::string& fileName)
  71. {
  72. validateFile(fileName);
  73. ifstream is(fileName.c_str());
  74. unsigned elementCount=0;
  75. std::map<string, unsigned> keywordCols;
  76. CsvElements data;
  77. bool firstLine(true);
  78. unsigned lineCount=0;
  79. while (!is.eof())
  80. {
  81. char line[1024];
  82. is.getline(line, sizeof(line));
  83. line[sizeof(line)-1] = 0;
  84. if(firstLine)
  85. {
  86. std::vector<string> header = csvLineToVector(line);
  87. elementCount = header.size();
  88. for(unsigned int i = 0; i < elementCount; i++)
  89. {
  90. keywordCols[header[i]] = i;
  91. }
  92. firstLine = false;
  93. }
  94. else // load the rest of the file
  95. {
  96. std::vector<string> parsedLine = csvLineToVector(line);
  97. if(parsedLine.size() != elementCount && parsedLine.size() !=0)
  98. {
  99. stringstream errorMsg;
  100. errorMsg << "Error at line " << lineCount+1 << ": expecting " << elementCount << " columns but read " << parsedLine.size() << " elements.";
  101. throw runtime_error(errorMsg.str());
  102. }
  103. for(unsigned int i = 0; i < parsedLine.size(); i++)
  104. {
  105. for(BOOST_AUTO(it,keywordCols.begin()); it!=keywordCols.end(); it++)
  106. {
  107. if(i == (*it).second)
  108. {
  109. data[(*it).first].push_back(parsedLine[i]);
  110. }
  111. }
  112. }
  113. }
  114. lineCount++;
  115. }
  116. // Use for debug
  117. //for(BOOST_AUTO(it,data.begin()); it!=data.end(); it++)
  118. //{
  119. // cout << "--------------------------" << endl;
  120. // cout << "Header: |" << (*it).first << "|" << endl;
  121. // //for(unsigned i=0; i<(*it).second.size(); i++)
  122. // //{
  123. // // cout << (*it).second[i] << endl;
  124. // //}
  125. //}
  126. return data;
  127. }
  128. //! Constructor, leave fields blank if unused
  129. template<typename T>
  130. PointMatcherIO<T>::FileInfo::FileInfo(const std::string& readingFileName, const std::string& referenceFileName, const std::string& configFileName, const TransformationParameters& initialTransformation, const TransformationParameters& groundTruthTransformation, const Vector& grativity):
  131. readingFileName(readingFileName),
  132. referenceFileName(referenceFileName),
  133. configFileName(configFileName),
  134. initialTransformation(initialTransformation),
  135. groundTruthTransformation(groundTruthTransformation),
  136. gravity(gravity)
  137. {}
  138. template struct PointMatcherIO<float>::FileInfo;
  139. template struct PointMatcherIO<double>::FileInfo;
  140. // Empty constructor
  141. template<typename T>
  142. PointMatcherIO<T>::FileInfoVector::FileInfoVector()
  143. {
  144. }
  145. //! Load a vector of FileInfo from a CSV file.
  146. /**
  147. @param fileName name of the CSV file
  148. @param dataPath path relative to which the point cloud CSV or VTK will be resolved
  149. @param configPath path relative to which the yaml configuration files will be resolved
  150. The first line of the CSV file must contain a header. The supported tags are:
  151. - reading: file name of the reading point cloud
  152. - reference: file name of the reference point cloud
  153. - config: file name of the YAML configuration of the ICP chain
  154. - iTxy: initial transformation, coordinate x,y
  155. - gTxy: ground-truth transformation, coordinate x,y
  156. Note that the header must at least contain "reading".
  157. */
  158. template<typename T>
  159. PointMatcherIO<T>::FileInfoVector::FileInfoVector(const std::string& fileName, std::string dataPath, std::string configPath)
  160. {
  161. if (dataPath.empty())
  162. {
  163. #if BOOST_FILESYSTEM_VERSION >= 3
  164. dataPath = boost::filesystem::path(fileName).parent_path().string();
  165. #else
  166. dataPath = boost::filesystem::path(fileName).parent_path().file_string();
  167. #endif
  168. }
  169. if (configPath.empty())
  170. {
  171. #if BOOST_FILESYSTEM_VERSION >= 3
  172. configPath = boost::filesystem::path(fileName).parent_path().string();
  173. #else
  174. configPath = boost::filesystem::path(fileName).parent_path().file_string();
  175. #endif
  176. }
  177. const CsvElements data = parseCsvWithHeader(fileName);
  178. // Look for transformations
  179. const bool found3dInitialTrans(findTransform(data, "iT", 3));
  180. bool found2dInitialTrans(findTransform(data, "iT", 2));
  181. const bool found3dGroundTruthTrans(findTransform(data, "gT", 3));
  182. bool found2dGroundTruthTrans(findTransform(data, "gT", 2));
  183. if (found3dInitialTrans)
  184. found2dInitialTrans = false;
  185. if (found3dGroundTruthTrans)
  186. found2dGroundTruthTrans = false;
  187. // Check for consistency
  188. if (found3dInitialTrans && found2dGroundTruthTrans)
  189. throw runtime_error("Initial transformation is in 3D but ground-truth is in 2D");
  190. if (found2dInitialTrans && found3dGroundTruthTrans)
  191. throw runtime_error("Initial transformation is in 2D but ground-truth is in 3D");
  192. CsvElements::const_iterator readingIt(data.find("reading"));
  193. if (readingIt == data.end())
  194. throw runtime_error("Error transfering CSV to structure: The header should at least contain \"reading\".");
  195. CsvElements::const_iterator referenceIt(data.find("reference"));
  196. CsvElements::const_iterator configIt(data.find("config"));
  197. // Load reading
  198. const std::vector<string>& readingFileNames = readingIt->second;
  199. const unsigned lineCount = readingFileNames.size();
  200. boost::optional<std::vector<string> > referenceFileNames;
  201. boost::optional<std::vector<string> > configFileNames;
  202. if (referenceIt != data.end())
  203. {
  204. referenceFileNames = referenceIt->second;
  205. assert (referenceFileNames->size() == lineCount);
  206. }
  207. if (configIt != data.end())
  208. {
  209. configFileNames = configIt->second;
  210. assert (configFileNames->size() == lineCount);
  211. }
  212. // for every lines
  213. for(unsigned line=0; line<lineCount; line++)
  214. {
  215. FileInfo info;
  216. // Files
  217. info.readingFileName = localToGlobalFileName(dataPath, readingFileNames[line]);
  218. if (referenceFileNames)
  219. info.referenceFileName = localToGlobalFileName(dataPath, (*referenceFileNames)[line]);
  220. if (configFileNames)
  221. info.configFileName = localToGlobalFileName(configPath, (*configFileNames)[line]);
  222. // Load transformations
  223. if(found3dInitialTrans)
  224. info.initialTransformation = getTransform(data, "iT", 3, line);
  225. if(found2dInitialTrans)
  226. info.initialTransformation = getTransform(data, "iT", 2, line);
  227. if(found3dGroundTruthTrans)
  228. info.groundTruthTransformation = getTransform(data, "gT", 3, line);
  229. if(found2dGroundTruthTrans)
  230. info.groundTruthTransformation = getTransform(data, "gT", 2, line);
  231. // Build the list
  232. this->push_back(info);
  233. }
  234. // Debug: Print the list
  235. /*for(unsigned i=0; i<list.size(); i++)
  236. {
  237. cout << "\n--------------------------" << endl;
  238. cout << "Sequence " << i << ":" << endl;
  239. cout << "Reading path: " << list[i].readingFileName << endl;
  240. cout << "Reference path: " << list[i].referenceFileName << endl;
  241. cout << "Extension: " << list[i].fileExtension << endl;
  242. cout << "Tranformation:\n" << list[i].initialTransformation << endl;
  243. cout << "Grativity:\n" << list[i].gravity << endl;
  244. }
  245. */
  246. }
  247. //! Join parentPath and fileName and return the result as a global path
  248. template<typename T>
  249. std::string PointMatcherIO<T>::FileInfoVector::localToGlobalFileName(const std::string& parentPath, const std::string& fileName)
  250. {
  251. std::string globalFileName(fileName);
  252. if (!boost::filesystem::exists(globalFileName))
  253. {
  254. const boost::filesystem::path globalFilePath(boost::filesystem::path(parentPath) / boost::filesystem::path(fileName));
  255. #if BOOST_FILESYSTEM_VERSION >= 3
  256. globalFileName = globalFilePath.string();
  257. #else
  258. globalFileName = globalFilePath.file_string();
  259. #endif
  260. }
  261. validateFile(globalFileName);
  262. return globalFileName;
  263. }
  264. //! Return whether there is a valid transformation named prefix in data
  265. template<typename T>
  266. bool PointMatcherIO<T>::FileInfoVector::findTransform(const CsvElements& data, const std::string& prefix, unsigned dim)
  267. {
  268. bool found(true);
  269. for(unsigned i=0; i<dim+1; i++)
  270. {
  271. for(unsigned j=0; j<dim+1; j++)
  272. {
  273. stringstream transName;
  274. transName << prefix << i << j;
  275. found = found && (data.find(transName.str()) != data.end());
  276. }
  277. }
  278. return found;
  279. }
  280. //! Return the transformation named prefix from data
  281. template<typename T>
  282. typename PointMatcherIO<T>::TransformationParameters PointMatcherIO<T>::FileInfoVector::getTransform(const CsvElements& data, const std::string& prefix, unsigned dim, unsigned line)
  283. {
  284. TransformationParameters transformation(TransformationParameters::Identity(dim+1, dim+1));
  285. for(unsigned i=0; i<dim+1; i++)
  286. {
  287. for(unsigned j=0; j<dim+1; j++)
  288. {
  289. stringstream transName;
  290. transName << prefix << i << j;
  291. CsvElements::const_iterator colIt(data.find(transName.str()));
  292. const T value = boost::lexical_cast<T> (colIt->second[line]);
  293. transformation(i,j) = value;
  294. }
  295. }
  296. return transformation;
  297. }
  298. template struct PointMatcherIO<float>::FileInfoVector;
  299. template struct PointMatcherIO<double>::FileInfoVector;
  300. //! Throw a runtime_error exception if fileName cannot be opened
  301. void PointMatcherSupport::validateFile(const std::string& fileName)
  302. {
  303. boost::filesystem::path fullPath(fileName);
  304. ifstream ifs(fileName.c_str());
  305. if (!ifs.good())
  306. #if BOOST_FILESYSTEM_VERSION >= 3
  307. #if BOOST_VERSION >= 105000
  308. throw runtime_error(string("Cannot open file ") + boost::filesystem::complete(fullPath).generic_string());
  309. #else
  310. throw runtime_error(string("Cannot open file ") + boost::filesystem3::complete(fullPath).generic_string());
  311. #endif
  312. #else
  313. throw runtime_error(string("Cannot open file ") + boost::filesystem::complete(fullPath).native_file_string());
  314. #endif
  315. }
  316. //! Load a point cloud from a file, determine format from extension
  317. template<typename T>
  318. typename PointMatcher<T>::DataPoints PointMatcher<T>::DataPoints::load(const std::string& fileName)
  319. {
  320. const boost::filesystem::path path(fileName);
  321. const string& ext(boost::filesystem::extension(path));
  322. if (boost::iequals(ext, ".vtk"))
  323. return PointMatcherIO<T>::loadVTK(fileName);
  324. else if (boost::iequals(ext, ".csv"))
  325. return PointMatcherIO<T>::loadCSV(fileName);
  326. else if (boost::iequals(ext, ".ply"))
  327. return PointMatcherIO<T>::loadPLY(fileName);
  328. else if (boost::iequals(ext, ".pcd"))
  329. return PointMatcherIO<T>::loadPCD(fileName);
  330. else
  331. throw runtime_error("loadAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
  332. }
  333. template
  334. PointMatcher<float>::DataPoints PointMatcher<float>::DataPoints::load(const std::string& fileName);
  335. template
  336. PointMatcher<double>::DataPoints PointMatcher<double>::DataPoints::load(const std::string& fileName);
  337. //! @brief Load comma separated values (csv) file
  338. //! @param fileName a string containing the path and the file name
  339. //!
  340. //! This loader has 3 behaviors since there is no official standard for
  341. //! csv files. A 2D or 3D point cloud will be created automatically if:
  342. //! - there is a header with columns named x, y and optionnaly z
  343. //! - there are only 2 or 3 columns in the file
  344. //!
  345. //! Otherwise, the user is asked to enter column id manually which might
  346. //! block automatic processing.
  347. template<typename T>
  348. typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(const std::string& fileName)
  349. {
  350. ifstream ifs(fileName.c_str());
  351. validateFile(fileName);
  352. return loadCSV(ifs);
  353. }
  354. template<typename T>
  355. typename PointMatcherIO<T>::DescAssociationMap PointMatcherIO<T>::getDescAssocationMap()
  356. {
  357. DescAssociationMap assoc_map = boost::assign::map_list_of
  358. ("nx", DescAssociationPair(0,"normals"))
  359. ("ny", DescAssociationPair(1,"normals"))
  360. ("nz", DescAssociationPair(2,"normals"))
  361. ("normal_x", DescAssociationPair(0,"normals"))
  362. ("normal_y", DescAssociationPair(1,"normals"))
  363. ("normal_z", DescAssociationPair(2,"normals"))
  364. ("densities", DescAssociationPair(0,"densities"))
  365. ("intensity", DescAssociationPair(0,"intensity"))
  366. ("red", DescAssociationPair(0,"color"))
  367. ("green", DescAssociationPair(1,"color"))
  368. ("blue", DescAssociationPair(2,"color"))
  369. ("alpha", DescAssociationPair(3,"color"))
  370. ("eigValues0", DescAssociationPair(0,"eigValues"))
  371. ("eigValues1", DescAssociationPair(1,"eigValues"))
  372. ("eigValues2", DescAssociationPair(2,"eigValues"))
  373. ("eigVectors0X", DescAssociationPair(0,"eigVectors"))
  374. ("eigVectors0Y", DescAssociationPair(1,"eigVectors"))
  375. ("eigVectors0Z",DescAssociationPair(2,"eigVectors"))
  376. ("eigVectors1X", DescAssociationPair(3,"eigVectors"))
  377. ("eigVectors1Y", DescAssociationPair(4,"eigVectors"))
  378. ("eigVectors1Z",DescAssociationPair(5,"eigVectors"))
  379. ("eigVectors2X", DescAssociationPair(6,"eigVectors"))
  380. ("eigVectors2Y", DescAssociationPair(7,"eigVectors"))
  381. ("eigVectors2Z",DescAssociationPair(8,"eigVectors"))
  382. ("normals", DescAssociationPair(0,"normals"))
  383. ("eigValues", DescAssociationPair(0,"eigValues"))
  384. ("eigVectors", DescAssociationPair(0,"eigVectors"))
  385. ("color", DescAssociationPair(0,"color"));
  386. return assoc_map;
  387. }
  388. template <typename T>
  389. bool PointMatcherIO<T>::colLabelRegistered(const std::string& colLabel)
  390. {
  391. return getDescAssocationMap().count(colLabel) > 0;
  392. }
  393. template <typename T>
  394. typename PointMatcherIO<T>::DescAssociationPair PointMatcherIO<T>::getDescAssociationPair(const std::string& colLabel)
  395. {
  396. return getDescAssocationMap().find(colLabel)->second;
  397. }
  398. template <typename T>
  399. std::string PointMatcherIO<T>::getColLabel(const Label& label, const int row)
  400. {
  401. std::string colLabel;
  402. if (label.text == "normals")
  403. {
  404. if (row == 0)
  405. {
  406. colLabel = "nx";
  407. }
  408. if (row == 1)
  409. {
  410. colLabel = "ny";
  411. }
  412. if (row == 2)
  413. {
  414. colLabel = "nz";
  415. }
  416. }
  417. else if (label.text == "color")
  418. {
  419. if (row == 0)
  420. {
  421. colLabel = "red";
  422. }
  423. if (row == 1)
  424. {
  425. colLabel = "green";
  426. }
  427. if (row == 2)
  428. {
  429. colLabel = "blue";
  430. }
  431. if (row == 3)
  432. colLabel = "alpha";
  433. }
  434. else if (label.text == "eigValues")
  435. {
  436. colLabel = "eigValues" + boost::lexical_cast<string>(row);
  437. }
  438. else if (label.text == "eigVectors")
  439. {
  440. // format: eigVectors<0-2><X-Z>
  441. colLabel = "eigVectors" + boost::lexical_cast<string>(row/3);
  442. int row_mod = row % 3;
  443. if (row_mod == 0)
  444. colLabel += "X";
  445. else if (row_mod == 1)
  446. colLabel += "Y";
  447. else if (row_mod == 2)
  448. colLabel += "Z";
  449. }
  450. else if (label.span == 1)
  451. {
  452. colLabel = label.text;
  453. }
  454. else
  455. colLabel = label.text + boost::lexical_cast<std::string>(row);
  456. return colLabel;
  457. }
  458. //! @brief Load comma separated values (csv) file
  459. //! @see loadCSV()
  460. template<typename T>
  461. typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(std::istream& is)
  462. {
  463. typedef typename DataPoints::Label Label;
  464. typedef typename DataPoints::Labels Labels;
  465. vector<T> xData;
  466. vector<T> yData;
  467. vector<T> zData;
  468. vector<T> padData;
  469. vector<string> header;
  470. vector<int> descColsToKeep; // Record of which columns will be read into a descriptor
  471. map<int,DescAssociationPair> colToDescPair;
  472. map<string,int> descLabelToNumRows;
  473. map<string,int> descLabelToStartingRows;
  474. vector<vector<T> > descCols;
  475. int numDescCols = 0;
  476. int dim(0);
  477. bool firstLine(true);
  478. bool hasHeader(false);
  479. Labels featureLabels, descriptorLabels;
  480. int xCol(-1);
  481. int yCol(-1);
  482. int zCol(-1);
  483. char delimiters[] = " \t,;";
  484. char *token;
  485. while (!is.eof())
  486. {
  487. //char line[1024];
  488. string line;
  489. safeGetLine(is, line);
  490. //is.getline(line, sizeof(line));
  491. //line[sizeof(line)-1] = 0;
  492. // Look for text header
  493. unsigned int len = strspn(line.c_str(), " ,+-.1234567890Ee");
  494. if(len != line.length())
  495. {
  496. //cout << "Header detected" << endl;
  497. hasHeader = true;
  498. }
  499. else
  500. {
  501. hasHeader = false;
  502. }
  503. // Count dimension using first line
  504. if(firstLine)
  505. {
  506. char tmpLine[1024];
  507. strcpy(tmpLine, line.c_str());
  508. char *brkt = 0;
  509. token = strtok_r(tmpLine, delimiters, &brkt);
  510. while (token)
  511. {
  512. dim++;
  513. // Load text header
  514. if(hasHeader)
  515. {
  516. header.push_back(string(token));
  517. }
  518. token = strtok_r(NULL, delimiters, &brkt);
  519. }
  520. if (hasHeader)
  521. {
  522. // Search for x, y and z tags
  523. for(unsigned int i = 0; i < header.size(); i++)
  524. {
  525. std::string colLabel = header[i];
  526. if(colLabel.compare("x") == 0)
  527. xCol = i;
  528. if(colLabel.compare("y") == 0)
  529. yCol = i;
  530. if(colLabel.compare("z") == 0)
  531. zCol = i;
  532. if(colLabelRegistered(colLabel))
  533. {
  534. descColsToKeep.push_back(i);
  535. DescAssociationPair associationPair = getDescAssociationPair(colLabel);
  536. colToDescPair[i] = associationPair;
  537. descLabelToNumRows[associationPair.second]++;
  538. }
  539. }
  540. // Do cumulative sum over number of descriptor rows per decriptor to get the starting
  541. // index row of reach descriptor
  542. int cumSum = 0;
  543. for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
  544. {
  545. descLabelToStartingRows[it->first] = cumSum;
  546. cumSum += it->second;
  547. }
  548. // allocate descriptor vectors
  549. numDescCols = descColsToKeep.size(); // number of descriptor vectors
  550. descCols.resize(numDescCols);
  551. if(xCol == -1 || yCol == -1)
  552. {
  553. for(unsigned int i = 0; i < header.size(); i++)
  554. {
  555. cout << "(" << i << ") " << header[i] << endl;
  556. }
  557. cout << endl << "Enter ID for x: ";
  558. cin >> xCol;
  559. cout << "Enter ID for y: ";
  560. cin >> yCol;
  561. cout << "Enter ID for z (-1 if 2D data): ";
  562. cin >> zCol;
  563. }
  564. }
  565. else
  566. {
  567. // Check if it is a simple file with only coordinates
  568. if (!(dim == 2 || dim == 3))
  569. {
  570. cout << "WARNING: " << dim << " columns detected. Not obvious which columns to load for x, y or z." << endl;
  571. cout << endl << "Enter column ID (starting from 0) for x: ";
  572. cin >> xCol;
  573. cout << "Enter column ID (starting from 0) for y: ";
  574. cin >> yCol;
  575. cout << "Enter column ID (starting from 0, -1 if 2D data) for z: ";
  576. cin >> zCol;
  577. }
  578. else
  579. {
  580. // Assume logical order...
  581. xCol = 0;
  582. yCol = 1;
  583. if(dim == 3)
  584. zCol = 2;
  585. }
  586. }
  587. if(zCol != -1)
  588. dim = 3;
  589. else
  590. dim = 2;
  591. }
  592. // Load data!
  593. char *brkt = 0;
  594. char line_c[1024];
  595. strcpy(line_c,line.c_str());
  596. token = strtok_r(line_c, delimiters, &brkt);
  597. int currentCol = 0;
  598. int d = 0; // descriptor vector iterator
  599. int nextDescCol = -1; // next descriptor column to be recorded
  600. if (numDescCols > 0)
  601. nextDescCol = descColsToKeep[0];
  602. while (token)
  603. {
  604. // Load data only if no text is on the line
  605. if(!hasHeader)
  606. {
  607. if(currentCol == xCol)
  608. xData.push_back(atof(token));
  609. if(currentCol == yCol)
  610. yData.push_back(atof(token));
  611. if(currentCol == zCol)
  612. zData.push_back(atof(token));
  613. if(currentCol == nextDescCol)
  614. {
  615. DescAssociationPair descPair = colToDescPair[nextDescCol];
  616. int startingRow = descLabelToStartingRows[descPair.second];
  617. descCols[startingRow + descPair.first].push_back(atof(token));
  618. d++;
  619. // check for next descriptor column, if there are no more than we will no longer check
  620. if (d < numDescCols)
  621. nextDescCol = descColsToKeep[d];
  622. else
  623. nextDescCol = -1;
  624. }
  625. }
  626. token = strtok_r(NULL, delimiters, &brkt);
  627. currentCol++;
  628. }
  629. // Add one for uniform coordinates
  630. padData.push_back(1);
  631. if (firstLine)
  632. {
  633. // create standard labels
  634. for (int i=0; i < dim; i++)
  635. {
  636. string text;
  637. text += char('x' + i);
  638. featureLabels.push_back(Label(text, 1));
  639. }
  640. featureLabels.push_back(Label("pad", 1));
  641. for(map<string,int>::const_iterator d_it = descLabelToNumRows.begin(); d_it != descLabelToNumRows.end(); d_it++)
  642. {
  643. descriptorLabels.push_back(Label(d_it->first,d_it->second));
  644. }
  645. }
  646. firstLine = false;
  647. }
  648. assert(xData.size() == yData.size());
  649. int nbPoints = xData.size();
  650. // Transfer loaded points in specific structure (eigen matrix)
  651. Matrix features(dim+1, nbPoints);
  652. Matrix descriptors(numDescCols, nbPoints);
  653. for(int i=0; i < nbPoints; i++)
  654. {
  655. features(0,i) = xData[i];
  656. features(1,i) = yData[i];
  657. if(dim == 3)
  658. {
  659. features(2,i) = zData[i];
  660. features(3,i) = 1;
  661. }
  662. else
  663. {
  664. features(2,i) = 1;
  665. }
  666. for (int d = 0; d < numDescCols; d++)
  667. {
  668. descriptors(d,i) = descCols[d][i];
  669. }
  670. }
  671. if (numDescCols > 0)
  672. {
  673. DataPoints dataPoints(features, featureLabels, descriptors, descriptorLabels);
  674. return dataPoints;
  675. }
  676. else
  677. {
  678. DataPoints dataPoints(features, featureLabels);
  679. return dataPoints;
  680. }
  681. //cout << "Loaded " << dataPoints.features.cols() << " points." << endl;
  682. //cout << "Find " << dataPoints.features.rows() << " dimensions." << endl;
  683. //cout << features << endl;
  684. }
  685. template
  686. PointMatcher<float>::DataPoints PointMatcherIO<float>::loadCSV(const std::string& fileName);
  687. template
  688. PointMatcher<double>::DataPoints PointMatcherIO<double>::loadCSV(const std::string& fileName);
  689. //! Save a point cloud to a file, determine format from extension
  690. template<typename T>
  691. void PointMatcher<T>::DataPoints::save(const std::string& fileName) const
  692. {
  693. const boost::filesystem::path path(fileName);
  694. const string& ext(boost::filesystem::extension(path));
  695. if (boost::iequals(ext, ".vtk"))
  696. return PointMatcherIO<T>::saveVTK(*this, fileName);
  697. else if (boost::iequals(ext, ".csv"))
  698. return PointMatcherIO<T>::saveCSV(*this, fileName);
  699. else if (boost::iequals(ext, ".ply"))
  700. return PointMatcherIO<T>::savePLY(*this, fileName);
  701. else if (boost::iequals(ext, ".pcd"))
  702. return PointMatcherIO<T>::savePCD(*this, fileName);
  703. else
  704. throw runtime_error("saveAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
  705. }
  706. template
  707. void PointMatcher<float>::DataPoints::save(const std::string& fileName) const;
  708. template
  709. void PointMatcher<double>::DataPoints::save(const std::string& fileName) const;
  710. //! Save point cloud to a file as CSV
  711. template<typename T>
  712. void PointMatcherIO<T>::saveCSV(const DataPoints& data, const std::string& fileName)
  713. {
  714. ofstream ofs(fileName.c_str());
  715. if (!ofs.good())
  716. throw runtime_error(string("Cannot open file ") + fileName);
  717. saveCSV(data, ofs);
  718. }
  719. //! Save point cloud to a stream as CSV
  720. template<typename T>
  721. void PointMatcherIO<T>::saveCSV(const DataPoints& data, std::ostream& os)
  722. {
  723. const int pointCount(data.features.cols());
  724. const int dimCount(data.features.rows());
  725. const int descDimCount(data.descriptors.rows());
  726. if (pointCount == 0)
  727. {
  728. cerr << "Warning, no points, doing nothing" << endl;
  729. return;
  730. }
  731. // write header
  732. for (int i = 0; i < dimCount - 1; i++)
  733. {
  734. os << data.featureLabels[i].text;
  735. if (!((i == (dimCount - 2)) && descDimCount == 0))
  736. os << ",";
  737. }
  738. int n = 0;
  739. for (int i = 0; i < data.descriptorLabels.size(); i++)
  740. {
  741. Label lab = data.descriptorLabels[i];
  742. for (int s = 0; s < lab.span; s++)
  743. {
  744. os << getColLabel(lab,s);
  745. if (n != (descDimCount - 1))
  746. os << ",";
  747. n++;
  748. }
  749. }
  750. os << "\n";
  751. // write points
  752. for (int p = 0; p < pointCount; ++p)
  753. {
  754. for (int i = 0; i < dimCount-1; ++i)
  755. {
  756. os << data.features(i, p);
  757. if(!((i == (dimCount - 2)) && descDimCount == 0))
  758. os << " , ";
  759. }
  760. for (int i = 0; i < descDimCount; i++)
  761. {
  762. os << data.descriptors(i,p);
  763. if (i != (descDimCount - 1))
  764. os << ",";
  765. }
  766. os << "\n";
  767. }
  768. }
  769. template
  770. void PointMatcherIO<float>::saveCSV(const DataPoints& data, const std::string& fileName);
  771. template
  772. void PointMatcherIO<double>::saveCSV(const DataPoints& data, const std::string& fileName);
  773. //! Load point cloud from a file as VTK
  774. template<typename T>
  775. typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(const std::string& fileName)
  776. {
  777. ifstream ifs(fileName.c_str());
  778. if (!ifs.good())
  779. throw runtime_error(string("Cannot open file ") + fileName);
  780. return loadVTK(ifs);
  781. }
  782. //! Load point cloud from a stream as VTK
  783. template<typename T>
  784. typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(std::istream& is)
  785. {
  786. //typedef typename DataPoints::Label Label;
  787. //typedef typename DataPoints::Labels Labels;
  788. DataPoints loadedPoints;
  789. // parse header
  790. string line;
  791. getline(is, line);
  792. if (line.find("# vtk DataFile Version") != 0)
  793. throw runtime_error(string("Wrong magic header, found ") + line);
  794. getline(is, line);
  795. getline(is, line);
  796. if (line != "ASCII")
  797. throw runtime_error(string("Wrong file type, expecting ASCII, found ") + line);
  798. getline(is, line);
  799. SupportedVTKDataTypes dataType;
  800. if (line == "DATASET POLYDATA")
  801. dataType = POLYDATA;
  802. else if (line == "DATASET UNSTRUCTURED_GRID")
  803. dataType = UNSTRUCTURED_GRID;
  804. else
  805. throw runtime_error(string("Wrong data type, expecting DATASET POLYDATA, found ") + line);
  806. // parse points and descriptors
  807. string fieldName;
  808. string name;
  809. int pointCount = 0;
  810. string type;
  811. while (is.good())
  812. {
  813. is >> fieldName;
  814. // load features
  815. if(fieldName == "POINTS")
  816. {
  817. is >> pointCount;
  818. is >> type;
  819. if(!(type == "float" || type == "double"))
  820. throw runtime_error(string("Field POINTS can only be of type double or float"));
  821. Matrix features(4, pointCount);
  822. for (int p = 0; p < pointCount; ++p)
  823. {
  824. is >> features(0, p);
  825. is >> features(1, p);
  826. is >> features(2, p);
  827. features(3, p) = 1.0;
  828. }
  829. loadedPoints.addFeature("x", features.row(0));
  830. loadedPoints.addFeature("y", features.row(1));
  831. loadedPoints.addFeature("z", features.row(2));
  832. loadedPoints.addFeature("pad", features.row(3));
  833. }
  834. //////////////////////////////////////////////////////////
  835. // Dataset type
  836. // POLYDATA
  837. else if(dataType == POLYDATA && fieldName == "VERTICES")
  838. {
  839. int size;
  840. int verticeSize;
  841. is >> size >> verticeSize;
  842. // Skip vertice definition
  843. for (int p = 0; p < size; p++)
  844. {
  845. getline(is, line);
  846. if(line == "")
  847. p--;
  848. }
  849. }
  850. else if(dataType == POLYDATA && fieldName == "LINES")
  851. {
  852. int size;
  853. int lineSize;
  854. is >> size >> lineSize;
  855. // Skip line definition
  856. for (int p = 0; p < size; p++)
  857. {
  858. getline(is, line);
  859. if(line == "")
  860. p--;
  861. }
  862. }
  863. else if(dataType == POLYDATA && fieldName == "POLYGONS")
  864. {
  865. int size;
  866. int polySize;
  867. is >> size >> polySize;
  868. // Skip line definition
  869. for (int p = 0; p < size; p++)
  870. {
  871. getline(is, line);
  872. if(line == "")
  873. p--;
  874. }
  875. }
  876. else if(dataType == POLYDATA && fieldName == "TRIANGLE_STRIPS")
  877. {
  878. int size;
  879. int stripSize;
  880. is >> size >> stripSize;
  881. // Skip line definition
  882. for (int p = 0; p < size; p++)
  883. {
  884. getline(is, line);
  885. if(line == "")
  886. p--;
  887. }
  888. }
  889. // Unstructure Grid
  890. else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELLS")
  891. {
  892. int size;
  893. int cellSize;
  894. is >> size >> cellSize;
  895. // Skip line definition
  896. for (int p = 0; p < size; p++)
  897. {
  898. getline(is, line);
  899. if(line == "")
  900. p--;
  901. }
  902. }
  903. else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELL_TYPES")
  904. {
  905. int size;
  906. int cellSize;
  907. is >> size >> cellSize;
  908. // Skip line definition
  909. for (int p = 0; p < size; p++)
  910. {
  911. getline(is, line);
  912. if(line == "")
  913. p--;
  914. }
  915. }
  916. //////////////////////////////////////////////////////////
  917. // Point data
  918. else if(fieldName == "POINT_DATA")
  919. {
  920. int descriptorCount;
  921. is >> descriptorCount;
  922. if(pointCount != descriptorCount)
  923. throw runtime_error(string("The size of POINTS is different than POINT_DATA"));
  924. }
  925. //////////////////////////////////////////////////////////
  926. // Field data is ignored
  927. else if (fieldName == "FIELD")
  928. {
  929. string fieldDataName;
  930. int fieldDataCount;
  931. is >> fieldDataName >> fieldDataCount;
  932. for (int f = 0; f < fieldDataCount; f++)
  933. {
  934. //getline(is, line);
  935. string fieldDataArrayName, fieldDataArrayType;
  936. int numComponents, numTuples;
  937. is >> fieldDataArrayName >> numComponents >> numTuples >> fieldDataArrayType;
  938. int t_val;
  939. for (int t = 0; t < numComponents * numTuples; t++ )
  940. {
  941. is >> t_val;
  942. }
  943. }
  944. }
  945. else // Load descriptors
  946. {
  947. // descriptor name
  948. is >> name;
  949. int dim = 0;
  950. bool skipLookupTable = false;
  951. if(fieldName == "SCALARS")
  952. {
  953. dim = 1;
  954. is >> type;
  955. skipLookupTable = true;
  956. }
  957. else if(fieldName == "VECTORS")
  958. {
  959. dim = 3;
  960. is >> type;
  961. }
  962. else if(fieldName == "TENSORS")
  963. {
  964. dim = 9;
  965. is >> type;
  966. }
  967. else if(fieldName == "NORMALS")
  968. {
  969. dim = 3;
  970. is >> type;
  971. }
  972. else if(fieldName == "COLOR_SCALARS")
  973. {
  974. is >> dim;
  975. type = "float";
  976. }
  977. else
  978. throw runtime_error(string("Unknown field name " + fieldName + ", expecting SCALARS, VECTORS, TENSORS, NORMALS or COLOR_SCALARS."));
  979. if(!(type == "float" || type == "double"))
  980. throw runtime_error(string("Field " + fieldName + " is " + type + " but can only be of type double or float"));
  981. // Skip LOOKUP_TABLE line
  982. if(skipLookupTable)
  983. {
  984. //FIXME: FP - why the first line is aways empty?
  985. getline(is, line);
  986. getline(is, line);
  987. }
  988. Matrix descriptor(dim, pointCount);
  989. for (int p = 0; p < pointCount; ++p)
  990. {
  991. for(int d = 0; d < dim; d++)
  992. {
  993. is >> descriptor(d, p);
  994. }
  995. }
  996. loadedPoints.addDescriptor(name, descriptor);
  997. }
  998. }
  999. return loadedPoints;
  1000. }
  1001. template
  1002. PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadVTK(const std::string& fileName);
  1003. template
  1004. PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadVTK(const std::string& fileName);
  1005. //! Save point cloud to a file as VTK
  1006. template<typename T>
  1007. void PointMatcherIO<T>::saveVTK(const DataPoints& data, const std::string& fileName)
  1008. {
  1009. typedef typename InspectorsImpl<T>::VTKFileInspector VTKInspector;
  1010. Parametrizable::Parameters param;
  1011. boost::assign::insert(param) ("baseFileName", "");
  1012. VTKInspector vtkInspector(param);
  1013. vtkInspector.dumpDataPoints(data, fileName);
  1014. }
  1015. template
  1016. void PointMatcherIO<float>::saveVTK(const PointMatcherIO<float>::DataPoints& data, const std::string& fileName);
  1017. template
  1018. void PointMatcherIO<double>::saveVTK(const PointMatcher<double>::DataPoints& data, const std::string& fileName);
  1019. //! @brief Load polygon file format (ply) file
  1020. //! @param fileName a string containing the path and the file name
  1021. //!
  1022. //! Note: that the PLY does not define a standard for point clouds
  1023. //! Warning: Binary PLY files are not supported, only ASCII
  1024. //! Only PLY files with elements named "vertex" are supported
  1025. //! "vertex" should have 2 or 3 properties names "x", "y", "z" to define features.
  1026. //!
  1027. template<typename T>
  1028. typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPLY(const std::string& fileName)
  1029. {
  1030. ifstream ifs(fileName.c_str());
  1031. if (!ifs.good())
  1032. throw runtime_error(string("Cannot open file ") + fileName);
  1033. return loadPLY(ifs);
  1034. }
  1035. template
  1036. PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPLY(const string& fileName);
  1037. template
  1038. PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPLY(const string& fileName);
  1039. //! @brief Load polygon file format (PLY) file
  1040. //! @see loadPLY()
  1041. template <typename T>
  1042. typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPLY(std::istream& is)
  1043. {
  1044. typedef typename DataPoints::Label Label;
  1045. typedef typename DataPoints::Labels Labels;
  1046. typedef vector<PLYElement*> Elements;
  1047. ///////////////////////////
  1048. // PARSE PLY HEADER
  1049. bool format_defined = false;
  1050. bool header_processed = false;
  1051. Elements elements;
  1052. PLYElementF element_f; // factory
  1053. PLYElement* current_element = NULL;
  1054. bool skip_props = false; // flag to skip properties if element is not supported
  1055. unsigned elem_offset = 0; // keep track of line position of elements that are supported
  1056. string line;
  1057. getline(is, line);
  1058. if (line.find("ply") != 0) {
  1059. throw runtime_error(string("PLY parse error: wrong magic header, found ") + line);
  1060. }
  1061. while (!header_processed)
  1062. {
  1063. if(!getline(is, line))
  1064. throw runtime_error("PLY parse error: reached end of file before end of header definition");
  1065. if ( line.empty() )
  1066. continue;
  1067. istringstream stringstream (line);
  1068. string keyword;
  1069. stringstream >> keyword;
  1070. // ignore comment
  1071. if (keyword == "comment") {
  1072. continue;
  1073. }
  1074. if (keyword == "format")
  1075. {
  1076. if (format_defined)
  1077. throw runtime_error("PLY parse error: format already defined");
  1078. string format_str, version_str;
  1079. stringstream >> format_str >> version_str;
  1080. if (format_str != "ascii" && format_str != "binary_little_endian" && format_str != "binary_big_endian")
  1081. throw runtime_error(string("PLY parse error: format ") + format_str + string(" is not supported"));
  1082. if (format_str == "binary_little_endian" || format_str == "binary_big_endian")
  1083. throw runtime_error(string("PLY parse error: binary PLY files are not supported"));
  1084. if (version_str != "1.0")
  1085. {
  1086. throw runtime_error(string("PLY parse error: version ") + version_str + string(" of ply is not supported"));
  1087. }
  1088. format_defined = true;
  1089. }
  1090. else if (keyword == "element")
  1091. {
  1092. if (current_element != NULL && current_element->getNumSupportedProperties() == 0)
  1093. {
  1094. LOG_WARNING_STREAM("PLY parse warning: no supported properties in element " << current_element->name);
  1095. }
  1096. string elem_name, elem_num_s;
  1097. stringstream >> elem_name >> elem_num_s;
  1098. unsigned elem_num;
  1099. try
  1100. {
  1101. elem_num = boost::lexical_cast<unsigned>(elem_num_s);
  1102. }
  1103. catch (boost::bad_lexical_cast& e)
  1104. {
  1105. throw runtime_error(string("PLY parse error: bad number of elements ") + elem_num_s + string(" for element ") + elem_name);
  1106. }
  1107. if (element_f.elementSupported(elem_name))
  1108. {
  1109. // Initialize current element
  1110. PLYElement* elem = element_f.createElement(elem_name, elem_num, elem_offset);
  1111. current_element = elem;
  1112. // check that element is not already defined
  1113. for (typename Elements::const_iterator it = elements.begin(); it != elements.end(); it++ )
  1114. {
  1115. if (**it == *elem) {
  1116. throw runtime_error(string("PLY parse error: element: ") + elem_name + string( "is already defined"));
  1117. }
  1118. }
  1119. elements.push_back(elem);
  1120. skip_props = false;
  1121. }
  1122. else
  1123. {
  1124. LOG_WARNING_STREAM("PLY parse warning: element " << elem_name << " not supported. Skipping.");
  1125. skip_props = true;
  1126. }
  1127. elem_offset += elem_num;
  1128. }
  1129. else if (keyword == "property")
  1130. {
  1131. if (current_element == NULL)
  1132. {
  1133. throw runtime_error("PLY parse error: property listed without defining an element");
  1134. }
  1135. if (skip_props)
  1136. continue;
  1137. string next, prop_type, prop_name;
  1138. stringstream >> next;
  1139. // PLY list property
  1140. if (next == "list")
  1141. {
  1142. string prop_idx_type;
  1143. stringstream >> prop_idx_type >> prop_type >> prop_name;
  1144. PLYProperty list_prop(prop_idx_type, prop_type, prop_name, current_element->total_props);
  1145. if (current_element->supportsProperty(list_prop))
  1146. {
  1147. current_element->addProperty(list_prop);
  1148. }
  1149. else
  1150. {
  1151. LOG_WARNING_STREAM("PLY parse error: element " << current_element->name
  1152. << " does not support property " << prop_name << " " << prop_type );
  1153. }
  1154. }
  1155. // PLY regular property
  1156. else
  1157. {
  1158. prop_type = next;
  1159. stringstream >> prop_name;
  1160. PLYProperty prop(prop_type, prop_name, current_element->total_props);
  1161. if (current_element->supportsProperty(prop))
  1162. {
  1163. current_element->addProperty(prop);
  1164. }
  1165. else
  1166. {
  1167. LOG_WARNING_STREAM("PLY parse error: element " << current_element->name <<
  1168. " does not support property " << prop_name << " " << prop_type );
  1169. }
  1170. }
  1171. current_element->total_props++;
  1172. }
  1173. else if (keyword == "end_header")
  1174. {
  1175. if (!format_defined)
  1176. {
  1177. throw runtime_error(string("PLY parse error: format not defined in header"));
  1178. }
  1179. if (elements.size() == 0)
  1180. {
  1181. throw runtime_error(string("PLY parse error: no elements defined in header"));
  1182. }
  1183. if (current_element != NULL && current_element->getNumSupportedProperties() == 0)
  1184. {
  1185. LOG_WARNING_STREAM("PLY parse warning: no supported properties in element " << current_element->name);
  1186. }
  1187. header_processed = true;
  1188. }
  1189. }
  1190. ///////////////////////////
  1191. // PARSE PLY DATA
  1192. Matrix features, descriptors;
  1193. Labels feat_labels, desc_labels;
  1194. int l = 0; // number of elements instances that were processed
  1195. for (typename Elements::const_iterator el_it = elements.begin(); el_it != elements.end(); el_it++)
  1196. {
  1197. vector<PLYProperty> feature_props = (*el_it)->getFeatureProps();
  1198. vector<PLYProperty> descriptor_props = (*el_it)->getDescriptorProps();
  1199. PLYDescPropMap descriptor_map = (*el_it)->getDescPropMap();
  1200. const unsigned n_points = (*el_it)->num;
  1201. const unsigned offset = (*el_it)->offset;
  1202. const unsigned n_feat = (*el_it)->getNumFeatures();
  1203. const unsigned n_desc = (*el_it)->getNumDescriptors();
  1204. const unsigned n_dprop = (*el_it)->getNumDescProp();
  1205. int feat_offset = features.rows();
  1206. int desc_offset = descriptors.rows();
  1207. // map to reorder descriptor properties into consecutive rows corresponding to descriptors
  1208. std::map<std::string,int> descPropToRow;
  1209. // Get labels
  1210. for (typename vector<PLYProperty>::const_iterator it = feature_props.begin(); it != feature_props.end(); it++ )
  1211. {
  1212. feat_labels.push_back(Label(it->name));
  1213. }
  1214. int r = 0;
  1215. for (typename PLYDescPropMap::const_iterator it = descriptor_map.begin(); it != descriptor_map.end(); it++ )
  1216. {
  1217. desc_labels.push_back(Label(it->first,it->second.size()));
  1218. BOOST_FOREACH(PLYProperty prop, it->second )
  1219. {
  1220. descPropToRow[prop.name] = r;
  1221. r++;
  1222. }
  1223. }
  1224. // Allocate features and descriptors matrix
  1225. // If there are more than one element, grow the features matrix
  1226. // to accommodate new features in this element
  1227. features.conservativeResize(n_feat+feat_offset,n_points);
  1228. if (n_desc > 0)
  1229. descriptors.conservativeResize(n_dprop+desc_offset,n_points);
  1230. while (l < (offset + n_points) )
  1231. {
  1232. if(!getline(is, line))
  1233. throw runtime_error("PLY parse error: expected more data lines");
  1234. if ( line.empty() )
  1235. continue;
  1236. istringstream ss (line);
  1237. string first_word, prop_s;
  1238. ss >> first_word;
  1239. // ignore comment
  1240. if (first_word == "comment") {
  1241. continue;
  1242. }
  1243. // move to offset line
  1244. if (l < offset)
  1245. {
  1246. l++; // increment line count
  1247. continue;
  1248. }
  1249. unsigned f = 0; // features dimension
  1250. unsigned d = 0; // descriptor dimension
  1251. for (int pr = 0; f < n_feat || d < n_dprop ; pr++)
  1252. {
  1253. unsigned next_f = feature_props[f].pos; // get next supported feature property column
  1254. int next_d = 0;
  1255. int next_d_r = 0;
  1256. if (n_desc > 0)
  1257. {
  1258. next_d = descriptor_props[d].pos; // get next supported descriptor property column
  1259. next_d_r = descPropToRow[descriptor_props[d].name]; // get row of next supported descriptor in desc matrix
  1260. }
  1261. else
  1262. next_d = -1;
  1263. T prop_val;
  1264. if (pr > 0)
  1265. {
  1266. if(!(ss >> prop_val))
  1267. throw runtime_error(string("PLY parse error: at line ") + boost::lexical_cast<string>(l));
  1268. }
  1269. else
  1270. prop_val = boost::lexical_cast<T>(first_word);
  1271. if (pr == next_f)
  1272. {
  1273. features(f+feat_offset,l-offset) = prop_val;
  1274. f++;
  1275. }
  1276. else if (pr == next_d)
  1277. {
  1278. descriptors(next_d_r+desc_offset,l-offset) = prop_val;
  1279. d++;
  1280. }
  1281. }
  1282. l++; // increment line count
  1283. }
  1284. }
  1285. // Add homogeneous coordinates padding
  1286. feat_labels.push_back(Label("pad"));
  1287. features.conservativeResize(features.rows()+1,features.cols());
  1288. features.row(features.rows()-1) = Vector::Ones(features.cols());
  1289. if (descriptors.rows() > 0)
  1290. {
  1291. DataPoints loadedPoints(features,feat_labels,descriptors,desc_labels);
  1292. return loadedPoints;
  1293. }
  1294. else
  1295. {
  1296. DataPoints loadedPoints(features,feat_labels);
  1297. return loadedPoints;
  1298. }
  1299. }
  1300. template<typename T>
  1301. void PointMatcherIO<T>::savePLY(const DataPoints& data,
  1302. const std::string& fileName)
  1303. {
  1304. typedef typename DataPoints::Label Label;
  1305. //typedef typename DataPoints::Labels Labels;
  1306. ofstream ofs(fileName.c_str());
  1307. if (!ofs.good())
  1308. throw runtime_error(string("Cannot open file ") + fileName);
  1309. const int pointCount(data.features.cols());
  1310. const int featCount(data.features.rows());
  1311. const int descRows(data.descriptors.rows());
  1312. if (pointCount == 0)
  1313. {
  1314. cerr << "Warning, no points, doing nothing" << endl;
  1315. return;
  1316. }
  1317. ofs << "ply\n" <<"format ascii 1.0\n";
  1318. ofs << "element vertex " << pointCount << "\n";
  1319. for (int f=0; f <(featCount-1); f++)
  1320. {
  1321. ofs << "property float " << data.featureLabels[f].text << "\n";
  1322. }
  1323. for (int i = 0; i < data.descriptorLabels.size(); i++)
  1324. {
  1325. Label lab = data.descriptorLabels[i];
  1326. for (int s = 0; s < lab.span; s++)
  1327. {
  1328. ofs << "property float " << getColLabel(lab,s) << "\n";
  1329. }
  1330. }
  1331. ofs << "end_header\n";
  1332. // write points
  1333. for (int p = 0; p < pointCount; ++p)
  1334. {
  1335. for (int f = 0; f < featCount - 1; ++f)
  1336. {
  1337. ofs << data.features(f, p);
  1338. if(!(f == featCount-2 && descRows == 0))
  1339. ofs << " ";
  1340. }
  1341. for (int d = 0; d < descRows; ++d)
  1342. {
  1343. ofs << data.descriptors(d, p);
  1344. if(d != descRows-1)
  1345. ofs << " ";
  1346. }
  1347. ofs << "\n";
  1348. }
  1349. ofs.close();
  1350. }
  1351. template
  1352. void PointMatcherIO<float>::savePLY(const DataPoints& data, const std::string& fileName);
  1353. template
  1354. void PointMatcherIO<double>::savePLY(const DataPoints& data, const std::string& fileName);
  1355. //! @(brief) Regular PLY property constructor
  1356. template<typename T>
  1357. PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& type,
  1358. const std::string& name, const unsigned pos, const bool is_feature) :
  1359. name(name), type(type), pos(pos), is_feature(is_feature) {
  1360. if (plyPropTypeValid(type))
  1361. {
  1362. is_list = false;
  1363. }
  1364. else
  1365. {
  1366. throw std::runtime_error(
  1367. std::string("PLY parse error: property type ") + type
  1368. + std::string(" for property ") + name
  1369. + std::string(" is invalid"));
  1370. }
  1371. }
  1372. //! @(brief) PLY property list constructor
  1373. template<typename T>
  1374. PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& idx_type,
  1375. const std::string& type, const std::string& name, const unsigned pos, const bool is_feature) :
  1376. name(name), type(type), idx_type(idx_type), pos(pos), is_feature(is_feature)
  1377. {
  1378. if (plyPropTypeValid(idx_type) && plyPropTypeValid(type)) {
  1379. is_list = true;
  1380. } else
  1381. throw std::runtime_error(
  1382. std::string("PLY parse error: property list type ") + idx_type
  1383. + std::string(" ") + type
  1384. + std::string(" for property ") + name
  1385. + std::string(" is invalid"));
  1386. }
  1387. template
  1388. class PointMatcherIO<float>::PLYElement;
  1389. template
  1390. class PointMatcherIO<double>::PLYElement;
  1391. template
  1392. class PointMatcherIO<float>::PLYProperty;
  1393. template
  1394. class PointMatcherIO<double>::PLYProperty;
  1395. template <typename T>
  1396. void PointMatcherIO<T>::PLYElement::addProperty(
  1397. PLYProperty& prop) {
  1398. if (supportsProperty(prop))
  1399. {
  1400. //prop.is_feature = (getPMType(prop) == FEATURE);
  1401. PMPropTypes pm_type = getPMType(prop);
  1402. if (pm_type == FEATURE)
  1403. {
  1404. features.push_back(prop);
  1405. }
  1406. else if (pm_type == DESCRIPTOR)
  1407. {
  1408. DescAssociationPair associationPair = getDescAssociationPair(prop.name);
  1409. // Handle shuffling of
  1410. // if property is in the right order, push back to the end of vector
  1411. std::vector<PLYProperty>* propList = &descriptor_map[associationPair.second];
  1412. // if property is to be put in a later place, you need to resize the vector first
  1413. if (associationPair.first >= propList->size())
  1414. {
  1415. propList->resize(associationPair.first + 1);
  1416. (*propList)[associationPair.first] = prop;
  1417. }
  1418. // if to be put before, no need to resize
  1419. else
  1420. (*propList)[associationPair.first] = prop;
  1421. descriptors.push_back(prop);
  1422. }
  1423. else
  1424. {
  1425. throw std::runtime_error("PLY parse error: tried at add an unsupported property");
  1426. }
  1427. }
  1428. else
  1429. throw std::runtime_error(
  1430. std::string("PLY parse error: property ") + prop.name
  1431. + std::string(" not supported by element ") + name);
  1432. }
  1433. template <typename T>
  1434. int PointMatcherIO<T>::PLYElement::getNumFeatures() const
  1435. {
  1436. return features.size();
  1437. }
  1438. template <typename T>
  1439. int PointMatcherIO<T>::PLYElement::getNumDescriptors() const
  1440. {
  1441. return descriptor_map.size();
  1442. }
  1443. template <typename T>
  1444. int PointMatcherIO<T>::PLYElement::getNumDescProp() const
  1445. {
  1446. return descriptors.size();
  1447. }
  1448. template <typename T>
  1449. const std::vector<typename PointMatcherIO<T>::PLYProperty>& PointMatcherIO<T>::PLYElement::getFeatureProps() const
  1450. {
  1451. return features;
  1452. }
  1453. template <typename T>
  1454. const std::vector<typename PointMatcherIO<T>::PLYProperty>& PointMatcherIO<T>::PLYElement::getDescriptorProps() const
  1455. {
  1456. return descriptors;
  1457. }
  1458. template <typename T>
  1459. const typename PointMatcherIO<T>::PLYDescPropMap& PointMatcherIO<T>::PLYElement::getDescPropMap() const
  1460. {
  1461. return descriptor_map;
  1462. }
  1463. template <typename T>
  1464. size_t PointMatcherIO<T>::PLYElement::getNumSupportedProperties() const {
  1465. return features.size() + descriptors.size() ;
  1466. }
  1467. template <typename T>
  1468. bool PointMatcherIO<T>::PLYElement::supportsProperty(const PLYProperty& prop) const
  1469. {
  1470. return getPMType(prop) != UNSUPPORTED;
  1471. }
  1472. template<typename T>
  1473. typename PointMatcherIO<T>::PLYElement::PMPropTypes PointMatcherIO<T>::PLYVertex::getPMType(const PLYProperty& prop) const
  1474. {
  1475. if (prop.name == "x" || prop.name == "y" || prop.name == "z")
  1476. return this->FEATURE;
  1477. else if (colLabelRegistered(prop.name))
  1478. return this->DESCRIPTOR;
  1479. else
  1480. return this->UNSUPPORTED;
  1481. }
  1482. template <typename T>
  1483. typename PointMatcherIO<T>::PLYElementF::ElementTypes PointMatcherIO<T>::PLYElementF::getElementType(const std::string& elem_name)
  1484. {
  1485. string lc = elem_name;
  1486. boost::algorithm::to_lower(lc);
  1487. if (lc == "vertex")
  1488. {
  1489. return VERTEX;
  1490. }
  1491. else
  1492. {
  1493. return UNSUPPORTED;
  1494. }
  1495. }
  1496. template <typename T>
  1497. bool PointMatcherIO<T>::PLYElementF::elementSupported(const std::string& elem_name)
  1498. {
  1499. return getElementType(elem_name) != UNSUPPORTED;
  1500. }
  1501. template<typename T>
  1502. typename PointMatcherIO<T>::PLYElement* PointMatcherIO<T>::PLYElementF::createElement(
  1503. const std::string& elem_name, const int elem_num, const unsigned offset) {
  1504. ElementTypes type = getElementType(elem_name);
  1505. if (type == VERTEX)
  1506. return new PLYVertex(elem_num, offset);
  1507. else
  1508. return NULL;
  1509. }
  1510. template<typename T>
  1511. bool PointMatcherIO<T>::plyPropTypeValid(const std::string& type) {
  1512. return (type == "char" || type == "uchar" || type == "short"
  1513. || type == "ushort" || type == "int" || type == "uint"
  1514. || type == "float" || type == "double");
  1515. }
  1516. template <typename T>
  1517. bool PointMatcherIO<T>::PLYElement::operator==(const PLYElement& rhs) const
  1518. {
  1519. return name == rhs.name;
  1520. }
  1521. template <typename T>
  1522. bool PointMatcherIO<T>::PLYProperty::operator==(const PLYProperty& rhs) const
  1523. {
  1524. return name == rhs.name && type == rhs.type;
  1525. }
  1526. //! @brief Load Point Cloud Library (pcd) file
  1527. //! @param fileName a string containing the path and the file name
  1528. template<typename T>
  1529. typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(const string& fileName) {
  1530. ifstream ifs(fileName.c_str());
  1531. if (!ifs.good())
  1532. throw runtime_error(string("Cannot open file ") + fileName);
  1533. return loadPCD(ifs);
  1534. }
  1535. template
  1536. PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(const string& fileName);
  1537. template
  1538. PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(const string& fileName);
  1539. //template
  1540. //PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(istream& is);
  1541. //template
  1542. //PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(istream& is);
  1543. //! @brief Load PCD file
  1544. //! @see loadPCD()
  1545. template<typename T>
  1546. typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(std::istream& is) {
  1547. typedef typename DataPoints::Label Label;
  1548. typedef typename DataPoints::Labels Labels;
  1549. size_t numFields = 0;
  1550. size_t numDataFields = 0; // takes into account the cound of each field for multi row descriptors
  1551. int xFieldCol = -1;
  1552. int yFieldCol = -1;
  1553. int zFieldCol = -1;
  1554. vector<int> descFieldsToKeep;
  1555. map<int,DescAssociationPair> colToDescPair;
  1556. map<string,int> descLabelToNumRows;
  1557. map<string,int>

Large files files are truncated, but you can click here to view the full file