00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "IO.h"
00037 #include "InspectorsImpl.h"
00038
00039
00040 #include "PointMatcherPrivate.h"
00041
00042 #include <iostream>
00043 #include <fstream>
00044 #include <stdexcept>
00045 #include <ctype.h>
00046 #include "boost/algorithm/string.hpp"
00047 #include "boost/filesystem.hpp"
00048 #include "boost/filesystem/path.hpp"
00049 #include "boost/filesystem/operations.hpp"
00050 #include "boost/lexical_cast.hpp"
00051 #include "boost/foreach.hpp"
00052
00053 #ifdef WIN32
00054 #define strtok_r strtok_s
00055 #endif // WIN32
00056
00057 using namespace std;
00058 using namespace PointMatcherSupport;
00059
00060
00061
00062 static std::vector<string> csvLineToVector(const char* line)
00063 {
00064 std::vector<string> parsedLine;
00065 char delimiters[] = " \t,;";
00066 char *token;
00067 char tmpLine[1024];
00068 char *brkt = 0;
00069 strcpy(tmpLine, line);
00070 token = strtok_r(tmpLine, delimiters, &brkt);
00071 if(line[0] != '%')
00072 {
00073 while (token)
00074 {
00075 parsedLine.push_back(string(token));
00076 token = strtok_r(NULL, delimiters, &brkt);
00077 }
00078 }
00079
00080 return parsedLine;
00081 }
00082
00083
00084 CsvElements parseCsvWithHeader(const std::string& fileName)
00085 {
00086 validateFile(fileName);
00087
00088 ifstream is(fileName.c_str());
00089
00090 unsigned elementCount=0;
00091 std::map<string, unsigned> keywordCols;
00092 CsvElements data;
00093
00094 bool firstLine(true);
00095 unsigned lineCount=0;
00096 while (!is.eof())
00097 {
00098 char line[1024];
00099 is.getline(line, sizeof(line));
00100 line[sizeof(line)-1] = 0;
00101
00102 if(firstLine)
00103 {
00104 std::vector<string> header = csvLineToVector(line);
00105
00106 elementCount = header.size();
00107 for(unsigned int i = 0; i < elementCount; i++)
00108 {
00109 keywordCols[header[i]] = i;
00110 }
00111
00112 firstLine = false;
00113 }
00114 else
00115 {
00116 std::vector<string> parsedLine = csvLineToVector(line);
00117 if(parsedLine.size() != elementCount && parsedLine.size() !=0)
00118 {
00119 stringstream errorMsg;
00120 errorMsg << "Error at line " << lineCount+1 << ": expecting " << elementCount << " columns but read " << parsedLine.size() << " elements.";
00121 throw runtime_error(errorMsg.str());
00122 }
00123
00124 for(unsigned int i = 0; i < parsedLine.size(); i++)
00125 {
00126 for(BOOST_AUTO(it,keywordCols.begin()); it!=keywordCols.end(); it++)
00127 {
00128 if(i == (*it).second)
00129 {
00130 data[(*it).first].push_back(parsedLine[i]);
00131 }
00132 }
00133 }
00134 }
00135
00136 lineCount++;
00137 }
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152 return data;
00153 }
00154
00155
00157 template<typename T>
00158 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):
00159 readingFileName(readingFileName),
00160 referenceFileName(referenceFileName),
00161 configFileName(configFileName),
00162 initialTransformation(initialTransformation),
00163 groundTruthTransformation(groundTruthTransformation),
00164 gravity(gravity)
00165 {}
00166
00167 template struct PointMatcherIO<float>::FileInfo;
00168 template struct PointMatcherIO<double>::FileInfo;
00169
00170
00171 template<typename T>
00172 PointMatcherIO<T>::FileInfoVector::FileInfoVector()
00173 {
00174 }
00175
00177
00190 template<typename T>
00191 PointMatcherIO<T>::FileInfoVector::FileInfoVector(const std::string& fileName, std::string dataPath, std::string configPath)
00192 {
00193 if (dataPath.empty())
00194 {
00195 #if BOOST_FILESYSTEM_VERSION >= 3
00196 dataPath = boost::filesystem::path(fileName).parent_path().string();
00197 #else
00198 dataPath = boost::filesystem::path(fileName).parent_path().file_string();
00199 #endif
00200 }
00201 if (configPath.empty())
00202 {
00203 #if BOOST_FILESYSTEM_VERSION >= 3
00204 configPath = boost::filesystem::path(fileName).parent_path().string();
00205 #else
00206 configPath = boost::filesystem::path(fileName).parent_path().file_string();
00207 #endif
00208 }
00209
00210 const CsvElements data = parseCsvWithHeader(fileName);
00211
00212
00213 const bool found3dInitialTrans(findTransform(data, "iT", 3));
00214 bool found2dInitialTrans(findTransform(data, "iT", 2));
00215 const bool found3dGroundTruthTrans(findTransform(data, "gT", 3));
00216 bool found2dGroundTruthTrans(findTransform(data, "gT", 2));
00217 if (found3dInitialTrans)
00218 found2dInitialTrans = false;
00219 if (found3dGroundTruthTrans)
00220 found2dGroundTruthTrans = false;
00221
00222
00223 if (found3dInitialTrans && found2dGroundTruthTrans)
00224 throw runtime_error("Initial transformation is in 3D but ground-truth is in 2D");
00225 if (found2dInitialTrans && found3dGroundTruthTrans)
00226 throw runtime_error("Initial transformation is in 2D but ground-truth is in 3D");
00227 CsvElements::const_iterator readingIt(data.find("reading"));
00228 if (readingIt == data.end())
00229 throw runtime_error("Error transfering CSV to structure: The header should at least contain \"reading\".");
00230 CsvElements::const_iterator referenceIt(data.find("reference"));
00231 CsvElements::const_iterator configIt(data.find("config"));
00232
00233
00234 const std::vector<string>& readingFileNames = readingIt->second;
00235 const unsigned lineCount = readingFileNames.size();
00236 boost::optional<std::vector<string> > referenceFileNames;
00237 boost::optional<std::vector<string> > configFileNames;
00238 if (referenceIt != data.end())
00239 {
00240 referenceFileNames = referenceIt->second;
00241 assert (referenceFileNames->size() == lineCount);
00242 }
00243 if (configIt != data.end())
00244 {
00245 configFileNames = configIt->second;
00246 assert (configFileNames->size() == lineCount);
00247 }
00248
00249
00250 for(unsigned line=0; line<lineCount; line++)
00251 {
00252 FileInfo info;
00253
00254
00255 info.readingFileName = localToGlobalFileName(dataPath, readingFileNames[line]);
00256 if (referenceFileNames)
00257 info.referenceFileName = localToGlobalFileName(dataPath, (*referenceFileNames)[line]);
00258 if (configFileNames)
00259 info.configFileName = localToGlobalFileName(configPath, (*configFileNames)[line]);
00260
00261
00262 if(found3dInitialTrans)
00263 info.initialTransformation = getTransform(data, "iT", 3, line);
00264 if(found2dInitialTrans)
00265 info.initialTransformation = getTransform(data, "iT", 2, line);
00266 if(found3dGroundTruthTrans)
00267 info.groundTruthTransformation = getTransform(data, "gT", 3, line);
00268 if(found2dGroundTruthTrans)
00269 info.groundTruthTransformation = getTransform(data, "gT", 2, line);
00270
00271
00272 this->push_back(info);
00273 }
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287 }
00288
00290 template<typename T>
00291 std::string PointMatcherIO<T>::FileInfoVector::localToGlobalFileName(const std::string& parentPath, const std::string& fileName)
00292 {
00293 std::string globalFileName(fileName);
00294 if (!boost::filesystem::exists(globalFileName))
00295 {
00296 const boost::filesystem::path globalFilePath(boost::filesystem::path(parentPath) / boost::filesystem::path(fileName));
00297 #if BOOST_FILESYSTEM_VERSION >= 3
00298 globalFileName = globalFilePath.string();
00299 #else
00300 globalFileName = globalFilePath.file_string();
00301 #endif
00302 }
00303 validateFile(globalFileName);
00304 return globalFileName;
00305 }
00306
00308 template<typename T>
00309 bool PointMatcherIO<T>::FileInfoVector::findTransform(const CsvElements& data, const std::string& prefix, unsigned dim)
00310 {
00311 bool found(true);
00312 for(unsigned i=0; i<dim+1; i++)
00313 {
00314 for(unsigned j=0; j<dim+1; j++)
00315 {
00316 stringstream transName;
00317 transName << prefix << i << j;
00318 found = found && (data.find(transName.str()) != data.end());
00319 }
00320 }
00321 return found;
00322 }
00323
00325 template<typename T>
00326 typename PointMatcherIO<T>::TransformationParameters PointMatcherIO<T>::FileInfoVector::getTransform(const CsvElements& data, const std::string& prefix, unsigned dim, unsigned line)
00327 {
00328 TransformationParameters transformation(TransformationParameters::Identity(dim+1, dim+1));
00329 for(unsigned i=0; i<dim+1; i++)
00330 {
00331 for(unsigned j=0; j<dim+1; j++)
00332 {
00333 stringstream transName;
00334 transName << prefix << i << j;
00335 CsvElements::const_iterator colIt(data.find(transName.str()));
00336 const T value = boost::lexical_cast<T> (colIt->second[line]);
00337 transformation(i,j) = value;
00338 }
00339 }
00340 return transformation;
00341 }
00342
00343 template struct PointMatcherIO<float>::FileInfoVector;
00344 template struct PointMatcherIO<double>::FileInfoVector;
00345
00347 void PointMatcherSupport::validateFile(const std::string& fileName)
00348 {
00349 boost::filesystem::path fullPath(fileName);
00350
00351 ifstream ifs(fileName.c_str());
00352 if (!ifs.good())
00353 #if BOOST_FILESYSTEM_VERSION >= 3
00354 #if BOOST_VERSION >= 105000
00355 throw runtime_error(string("Cannot open file ") + boost::filesystem::complete(fullPath).generic_string());
00356 #else
00357 throw runtime_error(string("Cannot open file ") + boost::filesystem3::complete(fullPath).generic_string());
00358 #endif
00359 #else
00360 throw runtime_error(string("Cannot open file ") + boost::filesystem::complete(fullPath).native_file_string());
00361 #endif
00362 }
00363
00364
00366 template<typename T>
00367 typename PointMatcher<T>::DataPoints PointMatcher<T>::DataPoints::load(const std::string& fileName)
00368 {
00369 const boost::filesystem::path path(fileName);
00370 const string& ext(boost::filesystem::extension(path));
00371 if (boost::iequals(ext, ".vtk"))
00372 return PointMatcherIO<T>::loadVTK(fileName);
00373 else if (boost::iequals(ext, ".csv"))
00374 return PointMatcherIO<T>::loadCSV(fileName);
00375 else if (boost::iequals(ext, ".ply"))
00376 return PointMatcherIO<T>::loadPLY(fileName);
00377 else if (boost::iequals(ext, ".pcd"))
00378 return PointMatcherIO<T>::loadPCD(fileName);
00379 else
00380 throw runtime_error("loadAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
00381 }
00382
00383 template
00384 PointMatcher<float>::DataPoints PointMatcher<float>::DataPoints::load(const std::string& fileName);
00385 template
00386 PointMatcher<double>::DataPoints PointMatcher<double>::DataPoints::load(const std::string& fileName);
00387
00388
00399 template<typename T>
00400 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(const std::string& fileName)
00401 {
00402 ifstream ifs(fileName.c_str());
00403
00404 validateFile(fileName);
00405
00406 return loadCSV(ifs);
00407 }
00408
00409 template<typename T>
00410 PointMatcherIO<T>::SupportedLabel::SupportedLabel(const std::string& internalName, const std::string& externalName, const PMPropTypes& type):
00411 internalName(internalName),
00412 externalName(externalName),
00413 type(type)
00414 {
00415 }
00416
00417 template<typename T>
00418 typename PointMatcherIO<T>::SublabelAssociationMap PointMatcherIO<T>::getFeatAssocationMap()
00419 {
00420 const SublabelAssociationMap assoc_map = boost::assign::map_list_of
00421 ("x", LabelAssociationPair(0,"x"))
00422 ("y", LabelAssociationPair(1,"y"))
00423 ("z", LabelAssociationPair(2,"z"))
00424 ("pad", LabelAssociationPair(3,"pad"));
00425 return assoc_map;
00426 }
00427
00428 template<typename T>
00429 typename PointMatcherIO<T>::SublabelAssociationMap PointMatcherIO<T>::getDescAssocationMap()
00430 {
00431 const SublabelAssociationMap assoc_map = boost::assign::map_list_of
00432 ("nx", LabelAssociationPair(0,"normals"))
00433 ("ny", LabelAssociationPair(1,"normals"))
00434 ("nz", LabelAssociationPair(2,"normals"))
00435 ("normal_x", LabelAssociationPair(0,"normals"))
00436 ("normal_y", LabelAssociationPair(1,"normals"))
00437 ("normal_z", LabelAssociationPair(2,"normals"))
00438 ("densities", LabelAssociationPair(0,"densities"))
00439 ("intensity", LabelAssociationPair(0,"intensity"))
00440 ("red", LabelAssociationPair(0,"color"))
00441 ("green", LabelAssociationPair(1,"color"))
00442 ("blue", LabelAssociationPair(2,"color"))
00443 ("alpha", LabelAssociationPair(3,"color"))
00444 ("eigValues0", LabelAssociationPair(0,"eigValues"))
00445 ("eigValues1", LabelAssociationPair(1,"eigValues"))
00446 ("eigValues2", LabelAssociationPair(2,"eigValues"))
00447 ("eigVectors0X", LabelAssociationPair(0,"eigVectors"))
00448 ("eigVectors0Y", LabelAssociationPair(1,"eigVectors"))
00449 ("eigVectors0Z",LabelAssociationPair(2,"eigVectors"))
00450 ("eigVectors1X", LabelAssociationPair(3,"eigVectors"))
00451 ("eigVectors1Y", LabelAssociationPair(4,"eigVectors"))
00452 ("eigVectors1Z",LabelAssociationPair(5,"eigVectors"))
00453 ("eigVectors2X", LabelAssociationPair(6,"eigVectors"))
00454 ("eigVectors2Y", LabelAssociationPair(7,"eigVectors"))
00455 ("eigVectors2Z",LabelAssociationPair(8,"eigVectors"))
00456 ("normals", LabelAssociationPair(0,"normals"))
00457 ("eigValues", LabelAssociationPair(0,"eigValues"))
00458 ("eigVectors", LabelAssociationPair(0,"eigVectors"))
00459 ("color", LabelAssociationPair(0,"color"));
00460 return assoc_map;
00461 }
00462
00463 template <typename T>
00464 bool PointMatcherIO<T>::featSublabelRegistered(const std::string& externalName)
00465 {
00466 return getFeatAssocationMap().count(externalName) > 0;
00467 }
00468
00469 template <typename T>
00470 bool PointMatcherIO<T>::descSublabelRegistered(const std::string& externalName)
00471 {
00472 return getDescAssocationMap().count(externalName) > 0;
00473 }
00474
00475 template <typename T>
00476 typename PointMatcherIO<T>::LabelAssociationPair PointMatcherIO<T>::getFeatAssociationPair(const std::string& externalName)
00477 {
00478 return getFeatAssocationMap().find(externalName)->second;
00479 }
00480
00481 template <typename T>
00482 typename PointMatcherIO<T>::LabelAssociationPair PointMatcherIO<T>::getDescAssociationPair(const std::string& externalName)
00483 {
00484 return getDescAssocationMap().find(externalName)->second;
00485 }
00486
00487 template<typename T>
00488 typename PointMatcherIO<T>::PMPropTypes PointMatcherIO<T>::getPMType(const std::string& externalName)
00489 {
00490 if (featSublabelRegistered(externalName))
00491 return FEATURE;
00492 else if (descSublabelRegistered(externalName))
00493 return DESCRIPTOR;
00494 else
00495 return UNSUPPORTED;
00496
00497 }
00498
00499
00500 template<typename T>
00501 void PointMatcherIO<T>::LabelGenerator::add(std::string internalName)
00502 {
00503 bool findLabel = false;
00504 for(size_t i=0; i<labels.size(); ++i)
00505 {
00506 if(internalName == labels[i].text)
00507 {
00508 labels[i].span++;
00509 findLabel = true;
00510 break;
00511 }
00512
00513 }
00514
00515 if(!findLabel)
00516 {
00517 labels.push_back(Label(internalName,1));
00518 }
00519 }
00520
00521
00522 template<typename T>
00523 typename PointMatcher<T>::DataPoints::Labels PointMatcherIO<T>::LabelGenerator::getLabels() const
00524 {
00525 return labels;
00526 }
00527
00528 template
00529 class PointMatcherIO<float>::LabelGenerator;
00530 template
00531 class PointMatcherIO<double>::LabelGenerator;
00532 template <typename T>
00533
00534
00535 std::string PointMatcherIO<T>::getColLabel(const Label& label, const int row)
00536 {
00537 std::string externalName;
00538 if (label.text == "normals")
00539 {
00540 if (row == 0)
00541 {
00542 externalName = "nx";
00543 }
00544 if (row == 1)
00545 {
00546 externalName = "ny";
00547 }
00548 if (row == 2)
00549 {
00550 externalName = "nz";
00551 }
00552 }
00553 else if (label.text == "color")
00554 {
00555 if (row == 0)
00556 {
00557 externalName = "red";
00558 }
00559 if (row == 1)
00560 {
00561 externalName = "green";
00562 }
00563 if (row == 2)
00564 {
00565 externalName = "blue";
00566 }
00567 if (row == 3)
00568 externalName = "alpha";
00569 }
00570 else if (label.text == "eigValues")
00571 {
00572 externalName = "eigValues" + boost::lexical_cast<string>(row);
00573 }
00574 else if (label.text == "eigVectors")
00575 {
00576
00577 externalName = "eigVectors" + boost::lexical_cast<string>(row/3);
00578
00579 int row_mod = row % 3;
00580 if (row_mod == 0)
00581 externalName += "X";
00582 else if (row_mod == 1)
00583 externalName += "Y";
00584 else if (row_mod == 2)
00585 externalName += "Z";
00586 }
00587 else if (label.span == 1)
00588 {
00589 externalName = label.text;
00590 }
00591 else
00592 externalName = label.text + boost::lexical_cast<std::string>(row);
00593
00594 return externalName;
00595 }
00596
00597
00600 template<typename T>
00601 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(std::istream& is)
00602 {
00603
00604
00605
00606 vector<T> xData;
00607 vector<T> yData;
00608 vector<T> zData;
00609 vector<T> padData;
00610 vector<string> header;
00611 vector<int> descColsToKeep;
00612
00613 map<int,LabelAssociationPair> colToDescPair;
00614 map<string,int> descLabelToNumRows;
00615 map<string,int> descLabelToStartingRows;
00616
00617 vector<vector<T> > descCols;
00618 int numDescCols = 0;
00619 int dim(0);
00620 bool firstLine(true);
00621 bool hasHeader(false);
00622 Labels featureLabels, descriptorLabels;
00623 int xCol(-1);
00624 int yCol(-1);
00625 int zCol(-1);
00626
00627 char delimiters[] = " \t,;";
00628 char *token;
00629 while (!is.eof())
00630 {
00631
00632 string line;
00633 safeGetLine(is, line);
00634
00635
00636
00637
00638 unsigned int len = strspn(line.c_str(), " ,+-.1234567890Ee");
00639 if(len != line.length())
00640 {
00641
00642 hasHeader = true;
00643 }
00644 else
00645 {
00646 hasHeader = false;
00647 }
00648
00649
00650 if(firstLine)
00651 {
00652
00653 char tmpLine[1024];
00654 strcpy(tmpLine, line.c_str());
00655 char *brkt = 0;
00656 token = strtok_r(tmpLine, delimiters, &brkt);
00657 while (token)
00658 {
00659 dim++;
00660
00661
00662 if(hasHeader)
00663 {
00664 header.push_back(string(token));
00665 }
00666
00667 token = strtok_r(NULL, delimiters, &brkt);
00668 }
00669
00670
00671 if (hasHeader)
00672 {
00673
00674 for(unsigned int i = 0; i < header.size(); i++)
00675 {
00676 std::string colLabel = header[i];
00677 if(colLabel.compare("x") == 0)
00678 xCol = i;
00679
00680 if(colLabel.compare("y") == 0)
00681 yCol = i;
00682
00683 if(colLabel.compare("z") == 0)
00684 zCol = i;
00685
00686 if(descSublabelRegistered(colLabel))
00687 {
00688 descColsToKeep.push_back(i);
00689 LabelAssociationPair associationPair = getDescAssociationPair(colLabel);
00690
00691 colToDescPair[i] = associationPair;
00692 descLabelToNumRows[associationPair.second]++;
00693 }
00694 }
00695
00696
00697
00698 int cumSum = 0;
00699 for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
00700 {
00701 descLabelToStartingRows[it->first] = cumSum;
00702 cumSum += it->second;
00703 }
00704
00705
00706 numDescCols = descColsToKeep.size();
00707 descCols.resize(numDescCols);
00708
00709 if(xCol == -1 || yCol == -1)
00710 {
00711 for(unsigned int i = 0; i < header.size(); i++)
00712 {
00713 cout << "(" << i << ") " << header[i] << endl;
00714 }
00715 cout << endl << "Enter ID for x: ";
00716 cin >> xCol;
00717 cout << "Enter ID for y: ";
00718 cin >> yCol;
00719 cout << "Enter ID for z (-1 if 2D data): ";
00720 cin >> zCol;
00721 }
00722 }
00723 else
00724 {
00725
00726 if (!(dim == 2 || dim == 3))
00727 {
00728 cout << "WARNING: " << dim << " columns detected. Not obvious which columns to load for x, y or z." << endl;
00729 cout << endl << "Enter column ID (starting from 0) for x: ";
00730 cin >> xCol;
00731 cout << "Enter column ID (starting from 0) for y: ";
00732 cin >> yCol;
00733 cout << "Enter column ID (starting from 0, -1 if 2D data) for z: ";
00734 cin >> zCol;
00735 }
00736 else
00737 {
00738
00739 xCol = 0;
00740 yCol = 1;
00741 if(dim == 3)
00742 zCol = 2;
00743 }
00744 }
00745
00746 if(zCol != -1)
00747 dim = 3;
00748 else
00749 dim = 2;
00750 }
00751
00752
00753 char *brkt = 0;
00754 char line_c[1024];
00755 strcpy(line_c,line.c_str());
00756 token = strtok_r(line_c, delimiters, &brkt);
00757 int currentCol = 0;
00758 int d = 0;
00759 int nextDescCol = -1;
00760 if (numDescCols > 0)
00761 nextDescCol = descColsToKeep[0];
00762
00763 while (token)
00764 {
00765
00766 if(!hasHeader)
00767 {
00768 if(currentCol == xCol)
00769 xData.push_back(atof(token));
00770 if(currentCol == yCol)
00771 yData.push_back(atof(token));
00772 if(currentCol == zCol)
00773 zData.push_back(atof(token));
00774 if(currentCol == nextDescCol)
00775 {
00776 LabelAssociationPair descPair = colToDescPair[nextDescCol];
00777 int startingRow = descLabelToStartingRows[descPair.second];
00778 descCols[startingRow + descPair.first].push_back(atof(token));
00779 d++;
00780
00781 if (d < numDescCols)
00782 nextDescCol = descColsToKeep[d];
00783 else
00784 nextDescCol = -1;
00785 }
00786 }
00787
00788 token = strtok_r(NULL, delimiters, &brkt);
00789 currentCol++;
00790 }
00791
00792
00793 padData.push_back(1);
00794
00795 if (firstLine)
00796 {
00797
00798 for (int i=0; i < dim; i++)
00799 {
00800 string text;
00801 text += char('x' + i);
00802 featureLabels.push_back(Label(text, 1));
00803 }
00804 featureLabels.push_back(Label("pad", 1));
00805
00806 for(map<string,int>::const_iterator d_it = descLabelToNumRows.begin(); d_it != descLabelToNumRows.end(); d_it++)
00807 {
00808 descriptorLabels.push_back(Label(d_it->first,d_it->second));
00809 }
00810 }
00811
00812 firstLine = false;
00813 }
00814
00815 assert(xData.size() == yData.size());
00816 int nbPoints = xData.size();
00817
00818
00819 Matrix features(dim+1, nbPoints);
00820 Matrix descriptors(numDescCols, nbPoints);
00821
00822 for(int i=0; i < nbPoints; i++)
00823 {
00824 features(0,i) = xData[i];
00825 features(1,i) = yData[i];
00826 if(dim == 3)
00827 {
00828 features(2,i) = zData[i];
00829 features(3,i) = 1;
00830 }
00831 else
00832 {
00833 features(2,i) = 1;
00834 }
00835
00836 for (int d = 0; d < numDescCols; d++)
00837 {
00838 descriptors(d,i) = descCols[d][i];
00839 }
00840 }
00841
00842 if (numDescCols > 0)
00843 {
00844 DataPoints dataPoints(features, featureLabels, descriptors, descriptorLabels);
00845 return dataPoints;
00846 }
00847 else
00848 {
00849 DataPoints dataPoints(features, featureLabels);
00850 return dataPoints;
00851 }
00852
00853
00854
00855
00856 }
00857
00858 template
00859 PointMatcher<float>::DataPoints PointMatcherIO<float>::loadCSV(const std::string& fileName);
00860 template
00861 PointMatcher<double>::DataPoints PointMatcherIO<double>::loadCSV(const std::string& fileName);
00862
00864 template<typename T>
00865 void PointMatcher<T>::DataPoints::save(const std::string& fileName) const
00866 {
00867 const boost::filesystem::path path(fileName);
00868 const string& ext(boost::filesystem::extension(path));
00869 if (boost::iequals(ext, ".vtk"))
00870 return PointMatcherIO<T>::saveVTK(*this, fileName);
00871 else if (boost::iequals(ext, ".csv"))
00872 return PointMatcherIO<T>::saveCSV(*this, fileName);
00873 else if (boost::iequals(ext, ".ply"))
00874 return PointMatcherIO<T>::savePLY(*this, fileName);
00875 else if (boost::iequals(ext, ".pcd"))
00876 return PointMatcherIO<T>::savePCD(*this, fileName);
00877 else
00878 throw runtime_error("saveAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
00879 }
00880
00881 template
00882 void PointMatcher<float>::DataPoints::save(const std::string& fileName) const;
00883 template
00884 void PointMatcher<double>::DataPoints::save(const std::string& fileName) const;
00885
00887 template<typename T>
00888 void PointMatcherIO<T>::saveCSV(const DataPoints& data, const std::string& fileName)
00889 {
00890 ofstream ofs(fileName.c_str());
00891 if (!ofs.good())
00892 throw runtime_error(string("Cannot open file ") + fileName);
00893 saveCSV(data, ofs);
00894 }
00895
00897 template<typename T>
00898 void PointMatcherIO<T>::saveCSV(const DataPoints& data, std::ostream& os)
00899 {
00900 const int pointCount(data.features.cols());
00901 const int dimCount(data.features.rows());
00902 const int descDimCount(data.descriptors.rows());
00903
00904 if (pointCount == 0)
00905 {
00906 cerr << "Warning, no points, doing nothing" << endl;
00907 return;
00908 }
00909
00910
00911 for (int i = 0; i < dimCount - 1; i++)
00912 {
00913 os << data.featureLabels[i].text;
00914
00915 if (!((i == (dimCount - 2)) && descDimCount == 0))
00916 os << ",";
00917 }
00918
00919 int n = 0;
00920 for (size_t i = 0; i < data.descriptorLabels.size(); i++)
00921 {
00922 Label lab = data.descriptorLabels[i];
00923 for (size_t s = 0; s < lab.span; s++)
00924 {
00925 os << getColLabel(lab,s);
00926 if (n != (descDimCount - 1))
00927 os << ",";
00928 n++;
00929 }
00930 }
00931
00932 os << "\n";
00933
00934
00935 for (int p = 0; p < pointCount; ++p)
00936 {
00937 for (int i = 0; i < dimCount-1; ++i)
00938 {
00939 os << data.features(i, p);
00940 if(!((i == (dimCount - 2)) && descDimCount == 0))
00941 os << " , ";
00942 }
00943
00944 for (int i = 0; i < descDimCount; i++)
00945 {
00946 os << data.descriptors(i,p);
00947 if (i != (descDimCount - 1))
00948 os << ",";
00949 }
00950 os << "\n";
00951 }
00952
00953 }
00954
00955 template
00956 void PointMatcherIO<float>::saveCSV(const DataPoints& data, const std::string& fileName);
00957 template
00958 void PointMatcherIO<double>::saveCSV(const DataPoints& data, const std::string& fileName);
00959
00961 template<typename T>
00962 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(const std::string& fileName)
00963 {
00964 ifstream ifs(fileName.c_str());
00965 if (!ifs.good())
00966 throw runtime_error(string("Cannot open file ") + fileName);
00967 return loadVTK(ifs);
00968 }
00969
00971 template<typename T>
00972 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(std::istream& is)
00973 {
00974
00975
00976
00977 DataPoints loadedPoints;
00978
00979
00980 string line;
00981 getline(is, line);
00982 if (line.find("# vtk DataFile Version") != 0)
00983 throw runtime_error(string("Wrong magic header, found ") + line);
00984 getline(is, line);
00985 getline(is, line);
00986 if (line != "ASCII")
00987 throw runtime_error(string("Wrong file type, expecting ASCII, found ") + line);
00988 getline(is, line);
00989
00990 SupportedVTKDataTypes dataType;
00991 if (line == "DATASET POLYDATA")
00992 dataType = POLYDATA;
00993 else if (line == "DATASET UNSTRUCTURED_GRID")
00994 dataType = UNSTRUCTURED_GRID;
00995 else
00996 throw runtime_error(string("Wrong data type, expecting DATASET POLYDATA, found ") + line);
00997
00998
00999
01000 string fieldName;
01001 string name;
01002 int pointCount = 0;
01003 string type;
01004 while (is.good())
01005 {
01006 is >> fieldName;
01007
01008
01009 if(fieldName == "POINTS")
01010 {
01011 is >> pointCount;
01012 is >> type;
01013
01014 if(!(type == "float" || type == "double"))
01015 throw runtime_error(string("Field POINTS can only be of type double or float"));
01016
01017 Matrix features(4, pointCount);
01018 for (int p = 0; p < pointCount; ++p)
01019 {
01020 is >> features(0, p);
01021 is >> features(1, p);
01022 is >> features(2, p);
01023 features(3, p) = 1.0;
01024 }
01025 loadedPoints.addFeature("x", features.row(0));
01026 loadedPoints.addFeature("y", features.row(1));
01027 loadedPoints.addFeature("z", features.row(2));
01028 loadedPoints.addFeature("pad", features.row(3));
01029 }
01030
01032
01033
01034 else if(dataType == POLYDATA && fieldName == "VERTICES")
01035 {
01036 int size;
01037 int verticeSize;
01038 is >> size >> verticeSize;
01039
01040 for (int p = 0; p < size; p++)
01041 {
01042 getline(is, line);
01043 if(line == "")
01044 p--;
01045 }
01046 }
01047
01048 else if(dataType == POLYDATA && fieldName == "LINES")
01049 {
01050 int size;
01051 int lineSize;
01052 is >> size >> lineSize;
01053
01054 for (int p = 0; p < size; p++)
01055 {
01056 getline(is, line);
01057 if(line == "")
01058 p--;
01059 }
01060 }
01061
01062 else if(dataType == POLYDATA && fieldName == "POLYGONS")
01063 {
01064 int size;
01065 int polySize;
01066 is >> size >> polySize;
01067
01068 for (int p = 0; p < size; p++)
01069 {
01070 getline(is, line);
01071 if(line == "")
01072 p--;
01073 }
01074 }
01075
01076 else if(dataType == POLYDATA && fieldName == "TRIANGLE_STRIPS")
01077 {
01078 int size;
01079 int stripSize;
01080 is >> size >> stripSize;
01081
01082 for (int p = 0; p < size; p++)
01083 {
01084 getline(is, line);
01085 if(line == "")
01086 p--;
01087 }
01088 }
01089
01090
01091 else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELLS")
01092 {
01093 int size;
01094 int cellSize;
01095 is >> size >> cellSize;
01096
01097 for (int p = 0; p < size; p++)
01098 {
01099 getline(is, line);
01100 if(line == "")
01101 p--;
01102 }
01103 }
01104 else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELL_TYPES")
01105 {
01106 int size;
01107 int cellSize;
01108 is >> size >> cellSize;
01109
01110 for (int p = 0; p < size; p++)
01111 {
01112 getline(is, line);
01113 if(line == "")
01114 p--;
01115 }
01116 }
01117
01119
01120 else if(fieldName == "POINT_DATA")
01121 {
01122 int descriptorCount;
01123 is >> descriptorCount;
01124 if(pointCount != descriptorCount)
01125 throw runtime_error(string("The size of POINTS is different than POINT_DATA"));
01126 }
01128
01129 else if (fieldName == "FIELD")
01130 {
01131 string fieldDataName;
01132 int fieldDataCount;
01133 is >> fieldDataName >> fieldDataCount;
01134
01135 for (int f = 0; f < fieldDataCount; f++)
01136 {
01137
01138 string fieldDataArrayName, fieldDataArrayType;
01139 int numComponents, numTuples;
01140 is >> fieldDataArrayName >> numComponents >> numTuples >> fieldDataArrayType;
01141
01142 int t_val;
01143 for (int t = 0; t < numComponents * numTuples; t++ )
01144 {
01145 is >> t_val;
01146 }
01147 }
01148 }
01149 else
01150 {
01151
01152 is >> name;
01153
01154 int dim = 0;
01155 bool skipLookupTable = false;
01156 if(fieldName == "SCALARS")
01157 {
01158 dim = 1;
01159 is >> type;
01160 skipLookupTable = true;
01161 }
01162 else if(fieldName == "VECTORS")
01163 {
01164 dim = 3;
01165 is >> type;
01166 }
01167 else if(fieldName == "TENSORS")
01168 {
01169 dim = 9;
01170 is >> type;
01171 }
01172 else if(fieldName == "NORMALS")
01173 {
01174 dim = 3;
01175 is >> type;
01176 }
01177 else if(fieldName == "COLOR_SCALARS")
01178 {
01179 is >> dim;
01180 type = "float";
01181 }
01182 else
01183 throw runtime_error(string("Unknown field name " + fieldName + ", expecting SCALARS, VECTORS, TENSORS, NORMALS or COLOR_SCALARS."));
01184
01185
01186 if(!(type == "float" || type == "double"))
01187 throw runtime_error(string("Field " + fieldName + " is " + type + " but can only be of type double or float"));
01188
01189
01190 if(skipLookupTable)
01191 {
01192
01193 getline(is, line);
01194 getline(is, line);
01195 }
01196
01197 Matrix descriptor(dim, pointCount);
01198 for (int p = 0; p < pointCount; ++p)
01199 {
01200 for(int d = 0; d < dim; d++)
01201 {
01202 is >> descriptor(d, p);
01203 }
01204 }
01205 loadedPoints.addDescriptor(name, descriptor);
01206 }
01207
01208 }
01209
01210 return loadedPoints;
01211 }
01212
01213 template
01214 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadVTK(const std::string& fileName);
01215 template
01216 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadVTK(const std::string& fileName);
01217
01218
01220 template<typename T>
01221 void PointMatcherIO<T>::saveVTK(const DataPoints& data, const std::string& fileName)
01222 {
01223 typedef typename InspectorsImpl<T>::VTKFileInspector VTKInspector;
01224
01225 Parametrizable::Parameters param;
01226 boost::assign::insert(param) ("baseFileName", "");
01227 VTKInspector vtkInspector(param);
01228 vtkInspector.dumpDataPoints(data, fileName);
01229 }
01230
01231
01232 template
01233 void PointMatcherIO<float>::saveVTK(const PointMatcherIO<float>::DataPoints& data, const std::string& fileName);
01234 template
01235 void PointMatcherIO<double>::saveVTK(const PointMatcher<double>::DataPoints& data, const std::string& fileName);
01236
01245 template<typename T>
01246 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPLY(const std::string& fileName)
01247 {
01248 ifstream ifs(fileName.c_str());
01249 if (!ifs.good())
01250 throw runtime_error(string("Cannot open file ") + fileName);
01251 return loadPLY(ifs);
01252 }
01253
01254 template
01255 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPLY(const string& fileName);
01256 template
01257 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPLY(const string& fileName);
01258
01261 template <typename T>
01262 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPLY(std::istream& is)
01263 {
01264
01265
01266 typedef vector<PLYElement*> Elements;
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01289
01290 bool format_defined = false;
01291 bool header_processed = false;
01292
01293 Elements elements;
01294 PLYElementF element_f;
01295 PLYElement* current_element = NULL;
01296 bool skip_props = false;
01297 unsigned elem_offset = 0;
01298 string line;
01299 getline(is, line);
01300
01301 if (line.find("ply") != 0) {
01302 throw runtime_error(string("PLY parse error: wrong magic header, found <") + line + string(">"));
01303 }
01304
01305 while (!header_processed)
01306 {
01307 if(!getline(is, line))
01308 throw runtime_error("PLY parse error: reached end of file before end of header definition");
01309
01310
01311 if ( line.empty() )
01312 continue;
01313 istringstream stringstream (line);
01314
01315 string keyword;
01316 stringstream >> keyword;
01317
01318
01319 if (keyword == "comment") {
01320 continue;
01321 }
01322
01323
01324 if (keyword == "format")
01325 {
01326 if (format_defined)
01327 throw runtime_error("PLY parse error: format already defined");
01328
01329 string format_str, version_str;
01330 stringstream >> format_str >> version_str;
01331
01332 if (format_str != "ascii" && format_str != "binary_little_endian" && format_str != "binary_big_endian")
01333 throw runtime_error(string("PLY parse error: format <") + format_str + string("> is not supported"));
01334
01335 if (format_str == "binary_little_endian" || format_str == "binary_big_endian")
01336 throw runtime_error(string("PLY parse error: binary PLY files are not supported"));
01337 if (version_str != "1.0")
01338 {
01339 throw runtime_error(string("PLY parse error: version <") + version_str + string("> of ply is not supported"));
01340 }
01341
01342 format_defined = true;
01343
01344 }
01345 else if (keyword == "element")
01346 {
01347
01348
01349 string elem_name, elem_num_s;
01350 stringstream >> elem_name >> elem_num_s;
01351
01352 unsigned elem_num;
01353 try
01354 {
01355 elem_num = boost::lexical_cast<unsigned>(elem_num_s);
01356 }
01357 catch (boost::bad_lexical_cast& e)
01358 {
01359 throw runtime_error(string("PLY parse error: bad number of elements ") + elem_num_s + string(" for element ") + elem_name);
01360 }
01361
01362 if (element_f.elementSupported(elem_name))
01363 {
01364
01365 PLYElement* elem = element_f.createElement(elem_name, elem_num, elem_offset);
01366 current_element = elem;
01367
01368
01369 for (typename Elements::const_iterator it = elements.begin(); it != elements.end(); it++ )
01370 {
01371 if (**it == *elem) {
01372 throw runtime_error(string("PLY parse error: element: ") + elem_name + string( "is already defined"));
01373 }
01374 }
01375 elements.push_back(elem);
01376 skip_props = false;
01377 }
01378 else
01379 {
01380 LOG_WARNING_STREAM("PLY parse warning: element " << elem_name << " not supported. Skipping.");
01381 skip_props = true;
01382 }
01383
01384 elem_offset += elem_num;
01385 }
01386 else if (keyword == "property")
01387 {
01388 if (current_element == NULL)
01389 {
01390 throw runtime_error("PLY parse error: property listed without defining an element");
01391 }
01392
01393 if (skip_props)
01394 continue;
01395
01396 string next, prop_type, prop_name;
01397 stringstream >> next;
01398
01399
01400 if (next == "list")
01401 {
01402 string prop_idx_type;
01403 stringstream >> prop_idx_type >> prop_type >> prop_name;
01404
01405 PLYProperty list_prop(prop_idx_type, prop_type, prop_name, current_element->total_props);
01406
01407 current_element->addProperty(list_prop);
01408 }
01409
01410 else
01411 {
01412 prop_type = next;
01413 stringstream >> prop_name;
01414 PLYProperty prop(prop_type, prop_name, current_element->total_props);
01415
01416 current_element->addProperty(prop);
01417 }
01418
01419 current_element->total_props++;
01420 }
01421 else if (keyword == "end_header")
01422 {
01423 if (!format_defined)
01424 {
01425 throw runtime_error(string("PLY parse error: format not defined in header"));
01426 }
01427
01428 if (elements.size() == 0)
01429 {
01430 throw runtime_error(string("PLY parse error: no elements defined in header"));
01431 }
01432
01433 header_processed = true;
01434 }
01435 }
01436
01438
01439
01440
01441 PLYElement* vertex = elements[0];
01442
01443 if(vertex->name != "vertex")
01444 {
01445 throw runtime_error(string("PLY parse error: vertex should be the first element defined."));
01446 }
01447
01448
01449 const SupportedLabels externalLabels = getSupportedExternalLabels();
01450
01451 int rowIdFeatures = 0;
01452 int rowIdDescriptors = 0;
01453
01454 LabelGenerator featLabelGen, descLabelGen;
01455
01456
01457 for(size_t i=0; i<externalLabels.size(); i++)
01458 {
01459 const SupportedLabel supLabel = externalLabels[i];
01460
01461
01462 for(it_PLYProp it=vertex->properties.begin(); it!=vertex->properties.end(); ++it)
01463 {
01464 if(supLabel.externalName == it->name)
01465 {
01466
01467 if(supLabel.type == FEATURE)
01468 {
01469 it->pmRowID = rowIdFeatures;
01470
01471
01472 featLabelGen.add(supLabel.internalName);
01473
01474 rowIdFeatures++;
01475 }
01476 else if (supLabel.type == DESCRIPTOR)
01477 {
01478 it->pmRowID = rowIdDescriptors;
01479
01480
01481 descLabelGen.add(supLabel.internalName);
01482
01483 rowIdDescriptors++;
01484 }
01485 else
01486 {
01487 throw runtime_error(string("PLY parse error: encounter a type different from FEATURE and DESCRIPTOR. Implementation not supported. See the definition of 'enum PMPropTypes'"));
01488 }
01489 break;
01490 }
01491 }
01492
01493
01494 }
01495
01497
01498
01499 const int featDim = featLabelGen.getLabels().totalDim();
01500 const int descDim = descLabelGen.getLabels().totalDim();
01501 const int nbPoints = vertex->num;
01502
01503 Matrix features = Matrix(featDim, nbPoints);
01504 Matrix descriptors = Matrix(descDim, nbPoints);
01505
01506
01507
01509
01510 const int nbProp = vertex->total_props;
01511 const int nbValues = nbPoints*nbProp;
01512 int propID = 0;
01513 int col = 0;
01514 for(int i=0; i<nbValues; i++)
01515 {
01516 T value;
01517 if(!(is >> value))
01518 {
01519 throw runtime_error(
01520 (boost::format("PLY parse error: expected %1% values (%2% points with %3% properties) but only found %4% values.") % nbValues % nbPoints % nbProp % i).str());
01521 }
01522 else
01523 {
01524 const int row = vertex->properties[propID].pmRowID;
01525 const PMPropTypes type = vertex->properties[propID].pmType;
01526
01527 if(type == FEATURE)
01528 {
01529 features(row, col) = value;
01530 }
01531 else if(type == DESCRIPTOR)
01532 {
01533 descriptors(row, col) = value;
01534 }
01535
01536 ++propID;
01537
01538 if(propID >= nbProp)
01539 {
01540 propID = 0;
01541 ++col;
01542 }
01543 }
01544 }
01545
01546
01547
01549
01550
01551 DataPoints loadedPoints;
01552
01553 if (descriptors.rows() > 0)
01554 {
01555 loadedPoints = DataPoints(features, featLabelGen.getLabels(),
01556 descriptors,descLabelGen.getLabels());
01557 }
01558 else
01559 {
01560 DataPoints loadedPoints(features, featLabelGen.getLabels());
01561 }
01562
01563
01564 if(!loadedPoints.featureExists("pad"))
01565 {
01566 loadedPoints.addFeature("pad", Matrix::Ones(1,nbPoints));
01567 }
01568
01569 return loadedPoints;
01570
01571 }
01572
01573 template<typename T>
01574 void PointMatcherIO<T>::savePLY(const DataPoints& data,
01575 const std::string& fileName)
01576 {
01577
01578
01579 ofstream ofs(fileName.c_str());
01580 if (!ofs.good())
01581 throw runtime_error(string("Cannot open file ") + fileName);
01582
01583 const int pointCount(data.features.cols());
01584 const int featCount(data.features.rows());
01585 const int descRows(data.descriptors.rows());
01586
01587
01588 if (pointCount == 0)
01589 {
01590 cerr << "Warning, no points, doing nothing" << endl;
01591 return;
01592 }
01593
01594 ofs << "ply\n" <<"format ascii 1.0\n";
01595 ofs << "element vertex " << pointCount << "\n";
01596 for (int f=0; f <(featCount-1); f++)
01597 {
01598 ofs << "property float " << data.featureLabels[f].text << "\n";
01599 }
01600
01601 for (size_t i = 0; i < data.descriptorLabels.size(); i++)
01602 {
01603 Label lab = data.descriptorLabels[i];
01604 for (size_t s = 0; s < lab.span; s++)
01605 {
01606 ofs << "property float " << getColLabel(lab,s) << "\n";
01607 }
01608 }
01609
01610 ofs << "end_header\n";
01611
01612
01613 for (int p = 0; p < pointCount; ++p)
01614 {
01615 for (int f = 0; f < featCount - 1; ++f)
01616 {
01617 ofs << data.features(f, p);
01618 if(!(f == featCount-2 && descRows == 0))
01619 ofs << " ";
01620 }
01621 for (int d = 0; d < descRows; ++d)
01622 {
01623 ofs << data.descriptors(d, p);
01624 if(d != descRows-1)
01625 ofs << " ";
01626 }
01627 ofs << "\n";
01628 }
01629
01630 ofs.close();
01631 }
01632
01633 template
01634 void PointMatcherIO<float>::savePLY(const DataPoints& data, const std::string& fileName);
01635 template
01636 void PointMatcherIO<double>::savePLY(const DataPoints& data, const std::string& fileName);
01637
01639 template<typename T>
01640 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& type,
01641 const std::string& name, const unsigned pos, const bool is_feature) :
01642 name(name),
01643 type(type),
01644 pos(pos),
01645 is_feature(is_feature)
01646 {
01647 if (plyPropTypeValid(type))
01648 {
01649 is_list = false;
01650 }
01651 else
01652 {
01653 throw std::runtime_error(
01654 std::string("PLY parse error: property type ") + type
01655 + std::string(" for property ") + name
01656 + std::string(" is invalid"));
01657 }
01658
01659 pmType = getPMType(name);
01660 pmRowID = -1;
01661
01662 }
01663
01665 template<typename T>
01666 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& idx_type,
01667 const std::string& type, const std::string& name, const unsigned pos, const bool is_feature) :
01668 name(name),
01669 type(type),
01670 idx_type(idx_type),
01671 pos(pos),
01672 is_feature(is_feature)
01673 {
01674 if (plyPropTypeValid(idx_type) && plyPropTypeValid(type))
01675 {
01676 is_list = true;
01677 }
01678 else
01679 {
01680 throw std::runtime_error(
01681 std::string("PLY parse error: property list type ") + idx_type
01682 + std::string(" ") + type
01683 + std::string(" for property ") + name
01684 + std::string(" is invalid"));
01685 }
01686
01687 pmType = getPMType(name);
01688 pmRowID = -1;
01689 }
01690
01691 template
01692 class PointMatcherIO<float>::PLYElement;
01693 template
01694 class PointMatcherIO<double>::PLYElement;
01695
01696 template
01697 class PointMatcherIO<float>::PLYProperty;
01698 template
01699 class PointMatcherIO<double>::PLYProperty;
01700
01701 template <typename T>
01702 void PointMatcherIO<T>::PLYElement::addProperty(
01703 PLYProperty& prop)
01704 {
01705 if (prop.pmType == FEATURE)
01706 {
01707 nbFeatures++;
01708 }
01709 else if (prop.pmType == DESCRIPTOR)
01710 {
01711 nbDescriptors++;
01712 }
01713
01714 properties.push_back(prop);
01715 }
01716
01717
01718 template <typename T>
01719 bool PointMatcherIO<T>::PLYElement::supportsProperty(const PLYProperty& prop) const
01720 {
01721 return getPMType(prop.name) != UNSUPPORTED;
01722 }
01723
01724
01725
01726 template <typename T>
01727 typename PointMatcherIO<T>::PLYElementF::ElementTypes PointMatcherIO<T>::PLYElementF::getElementType(const std::string& elem_name)
01728 {
01729 string lc = elem_name;
01730 boost::algorithm::to_lower(lc);
01731 if (lc == "vertex")
01732 {
01733 return VERTEX;
01734 }
01735 else
01736 {
01737 return UNSUPPORTED;
01738 }
01739 }
01740
01741 template <typename T>
01742 bool PointMatcherIO<T>::PLYElementF::elementSupported(const std::string& elem_name)
01743 {
01744 return getElementType(elem_name) != UNSUPPORTED;
01745 }
01746
01747 template<typename T>
01748 typename PointMatcherIO<T>::PLYElement* PointMatcherIO<T>::PLYElementF::createElement(
01749 const std::string& elem_name, const int elem_num, const unsigned offset) {
01750 ElementTypes type = getElementType(elem_name);
01751 if (type == VERTEX)
01752 return new PLYVertex(elem_num, offset);
01753 else
01754 return NULL;
01755 }
01756
01757 template<typename T>
01758 bool PointMatcherIO<T>::plyPropTypeValid(const std::string& type) {
01759 return (type == "char" || type == "uchar" || type == "short"
01760 || type == "ushort" || type == "int" || type == "uint"
01761 || type == "float" || type == "double");
01762 }
01763
01764 template <typename T>
01765 bool PointMatcherIO<T>::PLYElement::operator==(const PLYElement& rhs) const
01766 {
01767 return name == rhs.name;
01768 }
01769
01770
01771 template <typename T>
01772 bool PointMatcherIO<T>::PLYProperty::operator==(const PLYProperty& rhs) const
01773 {
01774 return name == rhs.name && type == rhs.type;
01775 }
01776
01779 template<typename T>
01780 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(const string& fileName) {
01781 ifstream ifs(fileName.c_str());
01782 if (!ifs.good())
01783 throw runtime_error(string("Cannot open file ") + fileName);
01784 return loadPCD(ifs);
01785 }
01786
01787 template
01788 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(const string& fileName);
01789 template
01790 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(const string& fileName);
01791
01792
01793
01794
01795
01796
01799 template<typename T>
01800 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(std::istream& is) {
01801
01802
01803
01804
01805 size_t numFields = 0;
01806 size_t numDataFields = 0;
01807 int xFieldCol = -1;
01808 int yFieldCol = -1;
01809 int zFieldCol = -1;
01810
01811 vector<int> descFieldsToKeep;
01812 map<int,LabelAssociationPair> colToDescPair;
01813 map<string,int> descLabelToNumRows;
01814 map<string,int> descLabelToStartingRows;
01815 vector<int> descDimensions;
01816
01817 string xFieldType;
01818 string yFieldType;
01819 string zFieldType;
01820
01821 size_t width = 0;
01822 size_t height = 0;
01823 size_t numPoints;
01824 size_t numPointsR;
01825
01826 size_t lineNum = 0;
01827
01828 while (!is.eof())
01829 {
01830 string line;
01831 getline(is, line);
01832
01833
01834 boost::trim (line);
01835
01836
01837 if (line.substr(0,1) == "#")
01838 {
01839 lineNum++;
01840 continue;
01841 }
01842
01843 vector<string> tokens;
01844 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
01845
01846 string pcd_version_str;
01847 if (tokens[0] == "VERSION")
01848 {
01849 if (tokens[1] != "0.7" && tokens[1] != ".7")
01850 throw runtime_error("PCD Parse Error: Only PCD Version 0.7 is supported");
01851 }
01852
01853 else if (tokens[0] == "FIELDS")
01854 {
01855 numFields = tokens.size() - 1;
01856 numDataFields = numFields;
01857 for (size_t i = 1; i < tokens.size(); i++)
01858 {
01859 if (tokens[i] == "x")
01860 xFieldCol = i - 1;
01861 else if (tokens[i] == "y")
01862 yFieldCol = i - 1;
01863 else if (tokens[i] == "z")
01864 zFieldCol = i - 1;
01865
01866 else if(descSublabelRegistered(tokens[i]))
01867 {
01868 descFieldsToKeep.push_back(i);
01869 LabelAssociationPair associationPair = getDescAssociationPair(tokens[i]);
01870
01871 colToDescPair[i] = associationPair;
01872 descLabelToNumRows[associationPair.second]++;
01873 }
01874 }
01875 }
01876
01877 else if (tokens[0] == "SIZE")
01878 {
01879 if (xFieldCol == -1 || yFieldCol == -1)
01880 throw runtime_error("PCD Parse Error: x field or y field not defined");
01881 if (tokens.size() - 1 != numFields)
01882 throw runtime_error("PCD Parse Error: size not defined for all fields");
01883
01884
01885
01886
01887
01888
01889
01890
01891
01892
01893
01894
01895 }
01896
01897 else if (tokens[0] == "TYPE")
01898 {
01899 if (xFieldCol == -1 || yFieldCol == -1)
01900 throw runtime_error("PCD Parse Error: x field or y field not defined");
01901 if (tokens.size() - 1 != numFields)
01902 throw runtime_error("PCD Parse Error: type not defined for all fields");
01903 xFieldType = tokens[xFieldCol + 1];
01904 yFieldType = tokens[yFieldCol + 1];
01905
01906 if (xFieldType != "I" && xFieldType != "U" && xFieldType != "F" &&
01907 yFieldType != "I" && yFieldType != "U" && yFieldType != "F")
01908 throw runtime_error("PCD Parse Error: invalid type");
01909
01910 if (zFieldCol > -1)
01911 {
01912 zFieldType = tokens[zFieldCol + 1];
01913 if (zFieldType != "I" && zFieldType != "U" && zFieldType != "F")
01914 throw runtime_error("PCD Parse Error: invalid type");
01915 }
01916 }
01917
01918
01919 else if (tokens[0] == "COUNT")
01920 {
01921 if (tokens.size() - 1 != numFields)
01922 throw runtime_error("PCD Parse Error: COUNT number does not match number of fields");
01923
01924
01925 numDataFields = 0;
01926
01927
01928
01929 map<int, LabelAssociationPair> colToDescPair_ = colToDescPair;
01930 colToDescPair.clear();
01931
01932 vector<int>::const_iterator nextFieldToKeepIt = descFieldsToKeep.begin();
01933 for (size_t i = 1; i < tokens.size(); i++)
01934 {
01935 int count = boost::lexical_cast<int>(tokens[i]);
01936
01937 if ((int)i == *nextFieldToKeepIt)
01938 {
01939 string descLabel = colToDescPair_[i].second;
01940 descLabelToNumRows[descLabel] = count;
01941
01942 for (int p = 0; p < count; p++)
01943 colToDescPair[numDataFields + p] = LabelAssociationPair(p, descLabel);
01944
01945 if (nextFieldToKeepIt != descFieldsToKeep.end())
01946 nextFieldToKeepIt++;
01947 }
01948
01949 numDataFields += count;
01950
01951 }
01952 }
01953
01954 else if (tokens[0] == "WIDTH")
01955 {
01956 try
01957 {
01958 width = boost::lexical_cast<int>(tokens[1]);
01959 } catch (boost::bad_lexical_cast& e)
01960 {
01961 throw runtime_error("PCD Parse Error: invalid width");
01962 }
01963 }
01964
01965 else if (tokens[0] == "HEIGHT")
01966 {
01967 try
01968 {
01969 height = boost::lexical_cast<int>(tokens[1]);
01970 } catch (boost::bad_lexical_cast& e)
01971 {
01972 throw runtime_error("PCD Parse Error: invalid width");
01973 }
01974 }
01975
01976
01977 else if (tokens[0] == "VIEWPOINT")
01978 {
01979 continue;
01980 }
01981
01982 else if (tokens[0] == "POINTS")
01983 {
01984 try
01985 {
01986 numPointsR = boost::lexical_cast<int>(tokens[1]);
01987 }
01988 catch (boost::bad_lexical_cast& e)
01989 {
01990 throw runtime_error("PCD Parse Error: invalid number of points");
01991 }
01992 }
01993
01994 else if (tokens[0] == "DATA")
01995 {
01996 if (tokens[1] != "ascii")
01997 throw runtime_error("PCD Parse Error: only ascii data is supported");
01998
01999 break;
02000 }
02001
02002 lineNum++;
02003 }
02004
02005
02006 numPoints = width * height;
02007
02008 if (numPoints != numPointsR)
02009 throw runtime_error("PCD Parse Error: POINTS field does not match WIDTH and HEIGHT fields");
02010
02011
02012 Matrix features;
02013 if (zFieldCol > -1)
02014 features = Matrix(4,numPoints);
02015 else
02016 features = Matrix(3,numPoints);
02017
02018
02019
02020
02021 int cumSum = 0;
02022 for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02023 {
02024 descLabelToStartingRows[it->first] = cumSum;
02025 cumSum += it->second;
02026 }
02027
02028
02029 size_t numDescCols = cumSum;
02030 Matrix descriptors(numDescCols,numPoints);
02031
02032
02033 size_t p = 0;
02034 while (!is.eof())
02035 {
02036 string line;
02037 getline(is, line);
02038
02039
02040 boost::trim (line);
02041
02042
02043 if (line.substr(0,1) == "#")
02044 {
02045 lineNum++;
02046 continue;
02047 }
02048
02049 vector<string> tokens;
02050 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
02051
02052 if (tokens.size() != numDataFields)
02053 throw runtime_error(string("PCD Parse Error: number of data columns does not match number of fields at line: ") + boost::lexical_cast<string>(lineNum));
02054
02055 features(0,p) = boost::lexical_cast<T>(tokens[xFieldCol]);
02056 features(1,p) = boost::lexical_cast<T>(tokens[yFieldCol]);
02057
02058 if (zFieldCol > -1)
02059 {
02060 features(2,p) = boost::lexical_cast<float>(tokens[zFieldCol]);
02061 features(3,p) = 1;
02062 } else
02063 features(2,p) = 1;
02064
02065 for (map<int,LabelAssociationPair>::const_iterator cit = colToDescPair.begin();
02066 cit != colToDescPair.end(); cit++)
02067 {
02068 int startingRow = descLabelToStartingRows[cit->second.second];
02069 descriptors(startingRow + cit->second.first,p) = boost::lexical_cast<T>(tokens[cit->first]);
02070 }
02071
02072 p++;
02073 lineNum++;
02074
02075 if (p == numPoints)
02076 break;
02077
02078 }
02079
02080 if (p != numPoints)
02081 {
02082 boost::format errorFmt("PCD Parse Error: the number of points in the data %1 is less than the specified number of points %2");
02083 errorFmt % p % numPoints;
02084 throw runtime_error(errorFmt.str());
02085 }
02086
02087 Labels featureLabels;
02088 featureLabels.push_back(Label("x"));
02089 featureLabels.push_back(Label("y"));
02090
02091 Labels descriptorLabels;
02092 int n = 0;
02093 for (map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02094 {
02095 descriptorLabels.push_back(Label(it->first,it->second));
02096 n++;
02097 }
02098
02099 if (zFieldCol > -1)
02100 featureLabels.push_back(Label("z"));
02101
02102 DataPoints out;
02103
02104 if (numDescCols > 0)
02105 out = DataPoints(features, featureLabels, descriptors, descriptorLabels);
02106 else
02107 out = DataPoints(features, featureLabels);
02108 return out;
02109 }
02110
02111 template<typename T>
02112 void PointMatcherIO<T>::savePCD(const DataPoints& data,
02113 const std::string& fileName) {
02114 ofstream ofs(fileName.c_str());
02115 if (!ofs.good())
02116 throw runtime_error(string("Cannot open file ") + fileName);
02117
02118 const int pointCount(data.features.cols());
02119 const int featCount(data.features.rows());
02120 const int descRows(data.descriptors.rows());
02121 const int descCount(data.descriptorLabels.size());
02122
02123 if (pointCount == 0)
02124 {
02125 cerr << "Warning, no points, doing nothing" << endl;
02126 return;
02127 }
02128
02129 ofs << "# .PCD v.7 - Point Cloud Data file format\n" <<"VERSION .7\n";
02130 ofs << "FIELDS";
02131
02132 for (int f=0; f < (featCount - 1); f++)
02133 {
02134 ofs << " " << data.featureLabels[f].text;
02135 }
02136
02137 if (descRows == 0)
02138 ofs << "\n";
02139 else
02140 {
02141 for (int i = 0; i < descCount; i++)
02142 {
02143 ofs << " " << data.descriptorLabels[i].text;
02144 }
02145 ofs << "\n";
02146 }
02147
02148 ofs << "SIZE";
02149 for (int i =0; i < featCount - 1 + descCount; i++)
02150 {
02151 ofs << " 4";
02152 }
02153 ofs << "\n";
02154
02155 ofs << "TYPE";
02156 for (int i =0; i < featCount - 1 + descCount; i++)
02157 {
02158 ofs << " F";
02159 }
02160 ofs << "\n";
02161
02162 ofs << "COUNT";
02163 for (int f = 0; f < featCount - 1 ; f++ )
02164 ofs << " 1";
02165 if (descCount == 0)
02166 ofs << "\n";
02167 else
02168 {
02169 for (int i = 0; i < descCount; i++)
02170 {
02171 ofs << " " << data.descriptorLabels[i].span;
02172 }
02173 ofs << "\n";
02174 }
02175
02176 ofs << "WIDTH " << pointCount << "\n";
02177 ofs << "HEIGHT 1\n";
02178 ofs << "POINTS " << pointCount << "\n";
02179 ofs << "DATA ascii\n";
02180
02181
02182 for (int p = 0; p < pointCount; ++p)
02183 {
02184 for (int f = 0; f < featCount - 1; ++f)
02185 {
02186 ofs << data.features(f, p);
02187 if(!(f == featCount-2 && descRows == 0))
02188 ofs << " ";
02189 }
02190 for (int d = 0; d < descRows; ++d)
02191 {
02192 ofs << data.descriptors(d, p);
02193 if(d != descRows-1)
02194 ofs << " ";
02195 }
02196 ofs << "\n";
02197 }
02198
02199 ofs.close();
02200 }
02201
02202 template
02203 void PointMatcherIO<float>::savePCD(const DataPoints& data, const std::string& fileName);
02204 template
02205 void PointMatcherIO<double>::savePCD(const DataPoints& data, const std::string& fileName);
02206
02207 template<typename T>
02208 istream & PointMatcherIO<T>::safeGetLine( istream& is, string & t)
02209 {
02210 t.clear();
02211
02212
02213
02214
02215
02216
02217
02218 std::istream::sentry se(is, true);
02219 std::streambuf* sb = is.rdbuf();
02220
02221 for(;;) {
02222 int c = sb->sbumpc();
02223 switch (c) {
02224 case '\n':
02225 return is;
02226 case '\r':
02227 if(sb->sgetc() == '\n')
02228 sb->sbumpc();
02229 return is;
02230 case EOF:
02231
02232 if(t.empty())
02233 is.setstate(std::ios::eofbit);
02234 return is;
02235 default:
02236 t += (char)c;
02237 }
02238 }
02239 }
02240
02241