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 (vertex->properties[propID].name == "red" || vertex->properties[propID].name == "green" || vertex->properties[propID].name == "blue" || vertex->properties[propID].name == "alpha") {
01528 value /= 255.0;
01529 }
01530
01531 if(type == FEATURE)
01532 {
01533 features(row, col) = value;
01534 }
01535 else if(type == DESCRIPTOR)
01536 {
01537 descriptors(row, col) = value;
01538 }
01539
01540 ++propID;
01541
01542 if(propID >= nbProp)
01543 {
01544 propID = 0;
01545 ++col;
01546 }
01547 }
01548 }
01549
01550
01551
01553
01554
01555 DataPoints loadedPoints;
01556
01557 if (descriptors.rows() > 0)
01558 {
01559 loadedPoints = DataPoints(features, featLabelGen.getLabels(),
01560 descriptors,descLabelGen.getLabels());
01561 }
01562 else
01563 {
01564 DataPoints loadedPoints(features, featLabelGen.getLabels());
01565 }
01566
01567
01568 if(!loadedPoints.featureExists("pad"))
01569 {
01570 loadedPoints.addFeature("pad", Matrix::Ones(1,nbPoints));
01571 }
01572
01573 return loadedPoints;
01574
01575 }
01576
01577 template<typename T>
01578 void PointMatcherIO<T>::savePLY(const DataPoints& data,
01579 const std::string& fileName)
01580 {
01581
01582
01583 ofstream ofs(fileName.c_str());
01584 if (!ofs.good())
01585 throw runtime_error(string("Cannot open file ") + fileName);
01586
01587 const int pointCount(data.features.cols());
01588 const int featCount(data.features.rows());
01589 const int descRows(data.descriptors.rows());
01590
01591
01592 if (pointCount == 0)
01593 {
01594 cerr << "Warning, no points, doing nothing" << endl;
01595 return;
01596 }
01597
01598 ofs << "ply\n" <<"format ascii 1.0\n";
01599 ofs << "element vertex " << pointCount << "\n";
01600 for (int f=0; f <(featCount-1); f++)
01601 {
01602 ofs << "property float " << data.featureLabels[f].text << "\n";
01603 }
01604
01605 for (size_t i = 0; i < data.descriptorLabels.size(); i++)
01606 {
01607 Label lab = data.descriptorLabels[i];
01608 for (size_t s = 0; s < lab.span; s++)
01609 {
01610 ofs << "property float " << getColLabel(lab,s) << "\n";
01611 }
01612 }
01613
01614 ofs << "end_header\n";
01615
01616
01617 for (int p = 0; p < pointCount; ++p)
01618 {
01619 for (int f = 0; f < featCount - 1; ++f)
01620 {
01621 ofs << data.features(f, p);
01622 if(!(f == featCount-2 && descRows == 0))
01623 ofs << " ";
01624 }
01625
01626 bool datawithColor = data.descriptorExists("color");
01627 int colorStartingRow = data.getDescriptorStartingRow("color");
01628 int colorEndRow = colorStartingRow + data.getDescriptorDimension("color");
01629 for (int d = 0; d < descRows; ++d)
01630 {
01631 if (datawithColor && d >= colorStartingRow && d < colorEndRow) {
01632 ofs << static_cast<unsigned>(data.descriptors(d, p) * 255.0);
01633 } else {
01634 ofs << data.descriptors(d, p);
01635 }
01636 if(d != descRows-1)
01637 ofs << " ";
01638 }
01639 ofs << "\n";
01640 }
01641
01642 ofs.close();
01643 }
01644
01645 template
01646 void PointMatcherIO<float>::savePLY(const DataPoints& data, const std::string& fileName);
01647 template
01648 void PointMatcherIO<double>::savePLY(const DataPoints& data, const std::string& fileName);
01649
01651 template<typename T>
01652 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& type,
01653 const std::string& name, const unsigned pos, const bool is_feature) :
01654 name(name),
01655 type(type),
01656 pos(pos),
01657 is_feature(is_feature)
01658 {
01659 if (plyPropTypeValid(type))
01660 {
01661 is_list = false;
01662 }
01663 else
01664 {
01665 throw std::runtime_error(
01666 std::string("PLY parse error: property type ") + type
01667 + std::string(" for property ") + name
01668 + std::string(" is invalid"));
01669 }
01670
01671 pmType = getPMType(name);
01672 pmRowID = -1;
01673
01674 }
01675
01677 template<typename T>
01678 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& idx_type,
01679 const std::string& type, const std::string& name, const unsigned pos, const bool is_feature) :
01680 name(name),
01681 type(type),
01682 idx_type(idx_type),
01683 pos(pos),
01684 is_feature(is_feature)
01685 {
01686 if (plyPropTypeValid(idx_type) && plyPropTypeValid(type))
01687 {
01688 is_list = true;
01689 }
01690 else
01691 {
01692 throw std::runtime_error(
01693 std::string("PLY parse error: property list type ") + idx_type
01694 + std::string(" ") + type
01695 + std::string(" for property ") + name
01696 + std::string(" is invalid"));
01697 }
01698
01699 pmType = getPMType(name);
01700 pmRowID = -1;
01701 }
01702
01703 template
01704 class PointMatcherIO<float>::PLYElement;
01705 template
01706 class PointMatcherIO<double>::PLYElement;
01707
01708 template
01709 class PointMatcherIO<float>::PLYProperty;
01710 template
01711 class PointMatcherIO<double>::PLYProperty;
01712
01713 template <typename T>
01714 void PointMatcherIO<T>::PLYElement::addProperty(
01715 PLYProperty& prop)
01716 {
01717 if (prop.pmType == FEATURE)
01718 {
01719 nbFeatures++;
01720 }
01721 else if (prop.pmType == DESCRIPTOR)
01722 {
01723 nbDescriptors++;
01724 }
01725
01726 properties.push_back(prop);
01727 }
01728
01729
01730 template <typename T>
01731 bool PointMatcherIO<T>::PLYElement::supportsProperty(const PLYProperty& prop) const
01732 {
01733 return getPMType(prop.name) != UNSUPPORTED;
01734 }
01735
01736
01737
01738 template <typename T>
01739 typename PointMatcherIO<T>::PLYElementF::ElementTypes PointMatcherIO<T>::PLYElementF::getElementType(const std::string& elem_name)
01740 {
01741 string lc = elem_name;
01742 boost::algorithm::to_lower(lc);
01743 if (lc == "vertex")
01744 {
01745 return VERTEX;
01746 }
01747 else
01748 {
01749 return UNSUPPORTED;
01750 }
01751 }
01752
01753 template <typename T>
01754 bool PointMatcherIO<T>::PLYElementF::elementSupported(const std::string& elem_name)
01755 {
01756 return getElementType(elem_name) != UNSUPPORTED;
01757 }
01758
01759 template<typename T>
01760 typename PointMatcherIO<T>::PLYElement* PointMatcherIO<T>::PLYElementF::createElement(
01761 const std::string& elem_name, const int elem_num, const unsigned offset) {
01762 ElementTypes type = getElementType(elem_name);
01763 if (type == VERTEX)
01764 return new PLYVertex(elem_num, offset);
01765 else
01766 return NULL;
01767 }
01768
01769 template<typename T>
01770 bool PointMatcherIO<T>::plyPropTypeValid(const std::string& type) {
01771 return (type == "char" || type == "uchar" || type == "short"
01772 || type == "ushort" || type == "int" || type == "uint"
01773 || type == "float" || type == "double");
01774 }
01775
01776 template <typename T>
01777 bool PointMatcherIO<T>::PLYElement::operator==(const PLYElement& rhs) const
01778 {
01779 return name == rhs.name;
01780 }
01781
01782
01783 template <typename T>
01784 bool PointMatcherIO<T>::PLYProperty::operator==(const PLYProperty& rhs) const
01785 {
01786 return name == rhs.name && type == rhs.type;
01787 }
01788
01791 template<typename T>
01792 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(const string& fileName) {
01793 ifstream ifs(fileName.c_str());
01794 if (!ifs.good())
01795 throw runtime_error(string("Cannot open file ") + fileName);
01796 return loadPCD(ifs);
01797 }
01798
01799 template
01800 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(const string& fileName);
01801 template
01802 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(const string& fileName);
01803
01804
01805
01806
01807
01808
01811 template<typename T>
01812 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(std::istream& is) {
01813
01814
01815
01816
01817 size_t numFields = 0;
01818 size_t numDataFields = 0;
01819 int xFieldCol = -1;
01820 int yFieldCol = -1;
01821 int zFieldCol = -1;
01822
01823 vector<int> descFieldsToKeep;
01824 map<int,LabelAssociationPair> colToDescPair;
01825 map<string,int> descLabelToNumRows;
01826 map<string,int> descLabelToStartingRows;
01827 vector<int> descDimensions;
01828
01829 string xFieldType;
01830 string yFieldType;
01831 string zFieldType;
01832
01833 size_t width = 0;
01834 size_t height = 0;
01835 size_t numPoints;
01836 size_t numPointsR = 0;
01837
01838 size_t lineNum = 0;
01839
01840 while (!is.eof())
01841 {
01842 string line;
01843 getline(is, line);
01844
01845
01846 boost::trim (line);
01847
01848
01849 if (line.substr(0,1) == "#")
01850 {
01851 lineNum++;
01852 continue;
01853 }
01854
01855 vector<string> tokens;
01856 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
01857
01858 string pcd_version_str;
01859 if (tokens[0] == "VERSION")
01860 {
01861 if (tokens[1] != "0.7" && tokens[1] != ".7")
01862 throw runtime_error("PCD Parse Error: Only PCD Version 0.7 is supported");
01863 }
01864
01865 else if (tokens[0] == "FIELDS")
01866 {
01867 numFields = tokens.size() - 1;
01868 numDataFields = numFields;
01869 for (size_t i = 1; i < tokens.size(); i++)
01870 {
01871 if (tokens[i] == "x")
01872 xFieldCol = i - 1;
01873 else if (tokens[i] == "y")
01874 yFieldCol = i - 1;
01875 else if (tokens[i] == "z")
01876 zFieldCol = i - 1;
01877
01878 else if(descSublabelRegistered(tokens[i]))
01879 {
01880 descFieldsToKeep.push_back(i);
01881 LabelAssociationPair associationPair = getDescAssociationPair(tokens[i]);
01882
01883 colToDescPair[i] = associationPair;
01884 descLabelToNumRows[associationPair.second]++;
01885 }
01886 }
01887 }
01888
01889 else if (tokens[0] == "SIZE")
01890 {
01891 if (xFieldCol == -1 || yFieldCol == -1)
01892 throw runtime_error("PCD Parse Error: x field or y field not defined");
01893 if (tokens.size() - 1 != numFields)
01894 throw runtime_error("PCD Parse Error: size not defined for all fields");
01895
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907 }
01908
01909 else if (tokens[0] == "TYPE")
01910 {
01911 if (xFieldCol == -1 || yFieldCol == -1)
01912 throw runtime_error("PCD Parse Error: x field or y field not defined");
01913 if (tokens.size() - 1 != numFields)
01914 throw runtime_error("PCD Parse Error: type not defined for all fields");
01915 xFieldType = tokens[xFieldCol + 1];
01916 yFieldType = tokens[yFieldCol + 1];
01917
01918 if (xFieldType != "I" && xFieldType != "U" && xFieldType != "F" &&
01919 yFieldType != "I" && yFieldType != "U" && yFieldType != "F")
01920 throw runtime_error("PCD Parse Error: invalid type");
01921
01922 if (zFieldCol > -1)
01923 {
01924 zFieldType = tokens[zFieldCol + 1];
01925 if (zFieldType != "I" && zFieldType != "U" && zFieldType != "F")
01926 throw runtime_error("PCD Parse Error: invalid type");
01927 }
01928 }
01929
01930
01931 else if (tokens[0] == "COUNT")
01932 {
01933 if (tokens.size() - 1 != numFields)
01934 throw runtime_error("PCD Parse Error: COUNT number does not match number of fields");
01935
01936
01937 numDataFields = 0;
01938
01939
01940
01941 map<int, LabelAssociationPair> colToDescPair_ = colToDescPair;
01942 colToDescPair.clear();
01943
01944 vector<int>::const_iterator nextFieldToKeepIt = descFieldsToKeep.begin();
01945 for (size_t i = 1; i < tokens.size(); i++)
01946 {
01947 int count = boost::lexical_cast<int>(tokens[i]);
01948
01949 if ((int)i == *nextFieldToKeepIt)
01950 {
01951 string descLabel = colToDescPair_[i].second;
01952 descLabelToNumRows[descLabel] = count;
01953
01954 for (int p = 0; p < count; p++)
01955 colToDescPair[numDataFields + p] = LabelAssociationPair(p, descLabel);
01956
01957 if (nextFieldToKeepIt != descFieldsToKeep.end())
01958 nextFieldToKeepIt++;
01959 }
01960
01961 numDataFields += count;
01962
01963 }
01964 }
01965
01966 else if (tokens[0] == "WIDTH")
01967 {
01968 try
01969 {
01970 width = boost::lexical_cast<int>(tokens[1]);
01971 } catch (boost::bad_lexical_cast& e)
01972 {
01973 throw runtime_error("PCD Parse Error: invalid width");
01974 }
01975 }
01976
01977 else if (tokens[0] == "HEIGHT")
01978 {
01979 try
01980 {
01981 height = boost::lexical_cast<int>(tokens[1]);
01982 } catch (boost::bad_lexical_cast& e)
01983 {
01984 throw runtime_error("PCD Parse Error: invalid width");
01985 }
01986 }
01987
01988
01989 else if (tokens[0] == "VIEWPOINT")
01990 {
01991 continue;
01992 }
01993
01994 else if (tokens[0] == "POINTS")
01995 {
01996 try
01997 {
01998 numPointsR = boost::lexical_cast<int>(tokens[1]);
01999 }
02000 catch (boost::bad_lexical_cast& e)
02001 {
02002 throw runtime_error("PCD Parse Error: invalid number of points");
02003 }
02004 }
02005
02006 else if (tokens[0] == "DATA")
02007 {
02008 if (tokens[1] != "ascii")
02009 throw runtime_error("PCD Parse Error: only ascii data is supported");
02010
02011 break;
02012 }
02013
02014 lineNum++;
02015 }
02016
02017
02018 numPoints = width * height;
02019
02020 if (numPoints != numPointsR)
02021 throw runtime_error("PCD Parse Error: POINTS field does not match WIDTH and HEIGHT fields");
02022
02023
02024 Matrix features;
02025 if (zFieldCol > -1)
02026 features = Matrix(4,numPoints);
02027 else
02028 features = Matrix(3,numPoints);
02029
02030
02031
02032
02033 int cumSum = 0;
02034 for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02035 {
02036 descLabelToStartingRows[it->first] = cumSum;
02037 cumSum += it->second;
02038 }
02039
02040
02041 size_t numDescCols = cumSum;
02042 Matrix descriptors(numDescCols,numPoints);
02043
02044
02045 size_t p = 0;
02046 while (!is.eof())
02047 {
02048 string line;
02049 getline(is, line);
02050
02051
02052 boost::trim (line);
02053
02054
02055 if (line.substr(0,1) == "#")
02056 {
02057 lineNum++;
02058 continue;
02059 }
02060
02061 vector<string> tokens;
02062 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
02063
02064 if (tokens.size() != numDataFields)
02065 throw runtime_error(string("PCD Parse Error: number of data columns does not match number of fields at line: ") + boost::lexical_cast<string>(lineNum));
02066
02067 features(0,p) = boost::lexical_cast<T>(tokens[xFieldCol]);
02068 features(1,p) = boost::lexical_cast<T>(tokens[yFieldCol]);
02069
02070 if (zFieldCol > -1)
02071 {
02072 features(2,p) = boost::lexical_cast<float>(tokens[zFieldCol]);
02073 features(3,p) = 1;
02074 } else
02075 features(2,p) = 1;
02076
02077 for (map<int,LabelAssociationPair>::const_iterator cit = colToDescPair.begin();
02078 cit != colToDescPair.end(); cit++)
02079 {
02080 int startingRow = descLabelToStartingRows[cit->second.second];
02081 descriptors(startingRow + cit->second.first,p) = boost::lexical_cast<T>(tokens[cit->first]);
02082 }
02083
02084 p++;
02085 lineNum++;
02086
02087 if (p == numPoints)
02088 break;
02089
02090 }
02091
02092 if (p != numPoints)
02093 {
02094 boost::format errorFmt("PCD Parse Error: the number of points in the data %1 is less than the specified number of points %2");
02095 errorFmt % p % numPoints;
02096 throw runtime_error(errorFmt.str());
02097 }
02098
02099 Labels featureLabels;
02100 featureLabels.push_back(Label("x"));
02101 featureLabels.push_back(Label("y"));
02102
02103 Labels descriptorLabels;
02104 int n = 0;
02105 for (map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02106 {
02107 descriptorLabels.push_back(Label(it->first,it->second));
02108 n++;
02109 }
02110
02111 if (zFieldCol > -1)
02112 featureLabels.push_back(Label("z"));
02113
02114 DataPoints out;
02115
02116 if (numDescCols > 0)
02117 out = DataPoints(features, featureLabels, descriptors, descriptorLabels);
02118 else
02119 out = DataPoints(features, featureLabels);
02120 return out;
02121 }
02122
02123 template<typename T>
02124 void PointMatcherIO<T>::savePCD(const DataPoints& data,
02125 const std::string& fileName) {
02126 ofstream ofs(fileName.c_str());
02127 if (!ofs.good())
02128 throw runtime_error(string("Cannot open file ") + fileName);
02129
02130 const int pointCount(data.features.cols());
02131 const int featCount(data.features.rows());
02132 const int descRows(data.descriptors.rows());
02133 const int descCount(data.descriptorLabels.size());
02134
02135 if (pointCount == 0)
02136 {
02137 cerr << "Warning, no points, doing nothing" << endl;
02138 return;
02139 }
02140
02141 ofs << "# .PCD v.7 - Point Cloud Data file format\n" <<"VERSION .7\n";
02142 ofs << "FIELDS";
02143
02144 for (int f=0; f < (featCount - 1); f++)
02145 {
02146 ofs << " " << data.featureLabels[f].text;
02147 }
02148
02149 if (descRows == 0)
02150 ofs << "\n";
02151 else
02152 {
02153 for (int i = 0; i < descCount; i++)
02154 {
02155 ofs << " " << data.descriptorLabels[i].text;
02156 }
02157 ofs << "\n";
02158 }
02159
02160 ofs << "SIZE";
02161 for (int i =0; i < featCount - 1 + descCount; i++)
02162 {
02163 ofs << " 4";
02164 }
02165 ofs << "\n";
02166
02167 ofs << "TYPE";
02168 for (int i =0; i < featCount - 1 + descCount; i++)
02169 {
02170 ofs << " F";
02171 }
02172 ofs << "\n";
02173
02174 ofs << "COUNT";
02175 for (int f = 0; f < featCount - 1 ; f++ )
02176 ofs << " 1";
02177 if (descCount == 0)
02178 ofs << "\n";
02179 else
02180 {
02181 for (int i = 0; i < descCount; i++)
02182 {
02183 ofs << " " << data.descriptorLabels[i].span;
02184 }
02185 ofs << "\n";
02186 }
02187
02188 ofs << "WIDTH " << pointCount << "\n";
02189 ofs << "HEIGHT 1\n";
02190 ofs << "POINTS " << pointCount << "\n";
02191 ofs << "DATA ascii\n";
02192
02193
02194 for (int p = 0; p < pointCount; ++p)
02195 {
02196 for (int f = 0; f < featCount - 1; ++f)
02197 {
02198 ofs << data.features(f, p);
02199 if(!(f == featCount-2 && descRows == 0))
02200 ofs << " ";
02201 }
02202 for (int d = 0; d < descRows; ++d)
02203 {
02204 ofs << data.descriptors(d, p);
02205 if(d != descRows-1)
02206 ofs << " ";
02207 }
02208 ofs << "\n";
02209 }
02210
02211 ofs.close();
02212 }
02213
02214 template
02215 void PointMatcherIO<float>::savePCD(const DataPoints& data, const std::string& fileName);
02216 template
02217 void PointMatcherIO<double>::savePCD(const DataPoints& data, const std::string& fileName);
02218
02219 template<typename T>
02220 istream & PointMatcherIO<T>::safeGetLine( istream& is, string & t)
02221 {
02222 t.clear();
02223
02224
02225
02226
02227
02228
02229
02230 std::istream::sentry se(is, true);
02231 std::streambuf* sb = is.rdbuf();
02232
02233 for(;;) {
02234 int c = sb->sbumpc();
02235 switch (c) {
02236 case '\n':
02237 return is;
02238 case '\r':
02239 if(sb->sgetc() == '\n')
02240 sb->sbumpc();
02241 return is;
02242 case EOF:
02243
02244 if(t.empty())
02245 is.setstate(std::ios::eofbit);
02246 return is;
02247 default:
02248 t += (char)c;
02249 }
02250 }
02251 }
02252
02253