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
00410 template<typename T>
00411 typename PointMatcherIO<T>::DescAssociationMap PointMatcherIO<T>::getDescAssocationMap()
00412 {
00413 DescAssociationMap assoc_map = boost::assign::map_list_of
00414 ("nx", DescAssociationPair(0,"normals"))
00415 ("ny", DescAssociationPair(1,"normals"))
00416 ("nz", DescAssociationPair(2,"normals"))
00417 ("normal_x", DescAssociationPair(0,"normals"))
00418 ("normal_y", DescAssociationPair(1,"normals"))
00419 ("normal_z", DescAssociationPair(2,"normals"))
00420 ("densities", DescAssociationPair(0,"densities"))
00421 ("intensity", DescAssociationPair(0,"intensity"))
00422 ("red", DescAssociationPair(0,"color"))
00423 ("green", DescAssociationPair(1,"color"))
00424 ("blue", DescAssociationPair(2,"color"))
00425 ("alpha", DescAssociationPair(3,"color"))
00426 ("eigValues0", DescAssociationPair(0,"eigValues"))
00427 ("eigValues1", DescAssociationPair(1,"eigValues"))
00428 ("eigValues2", DescAssociationPair(2,"eigValues"))
00429 ("eigVectors0X", DescAssociationPair(0,"eigVectors"))
00430 ("eigVectors0Y", DescAssociationPair(1,"eigVectors"))
00431 ("eigVectors0Z",DescAssociationPair(2,"eigVectors"))
00432 ("eigVectors1X", DescAssociationPair(3,"eigVectors"))
00433 ("eigVectors1Y", DescAssociationPair(4,"eigVectors"))
00434 ("eigVectors1Z",DescAssociationPair(5,"eigVectors"))
00435 ("eigVectors2X", DescAssociationPair(6,"eigVectors"))
00436 ("eigVectors2Y", DescAssociationPair(7,"eigVectors"))
00437 ("eigVectors2Z",DescAssociationPair(8,"eigVectors"))
00438 ("normals", DescAssociationPair(0,"normals"))
00439 ("eigValues", DescAssociationPair(0,"eigValues"))
00440 ("eigVectors", DescAssociationPair(0,"eigVectors"))
00441 ("color", DescAssociationPair(0,"color"));
00442 return assoc_map;
00443 }
00444
00445 template <typename T>
00446 bool PointMatcherIO<T>::colLabelRegistered(const std::string& colLabel)
00447 {
00448 return getDescAssocationMap().count(colLabel) > 0;
00449 }
00450
00451 template <typename T>
00452 typename PointMatcherIO<T>::DescAssociationPair PointMatcherIO<T>::getDescAssociationPair(const std::string& colLabel)
00453 {
00454 return getDescAssocationMap().find(colLabel)->second;
00455 }
00456
00457 template <typename T>
00458 std::string PointMatcherIO<T>::getColLabel(const Label& label, const int row)
00459 {
00460 std::string colLabel;
00461 if (label.text == "normals")
00462 {
00463 if (row == 0)
00464 {
00465 colLabel = "nx";
00466 }
00467 if (row == 1)
00468 {
00469 colLabel = "ny";
00470 }
00471 if (row == 2)
00472 {
00473 colLabel = "nz";
00474 }
00475 }
00476 else if (label.text == "color")
00477 {
00478 if (row == 0)
00479 {
00480 colLabel = "red";
00481 }
00482 if (row == 1)
00483 {
00484 colLabel = "green";
00485 }
00486 if (row == 2)
00487 {
00488 colLabel = "blue";
00489 }
00490 if (row == 3)
00491 colLabel = "alpha";
00492 }
00493 else if (label.text == "eigValues")
00494 {
00495 colLabel = "eigValues" + boost::lexical_cast<string>(row);
00496 }
00497 else if (label.text == "eigVectors")
00498 {
00499
00500 colLabel = "eigVectors" + boost::lexical_cast<string>(row/3);
00501
00502 int row_mod = row % 3;
00503 if (row_mod == 0)
00504 colLabel += "X";
00505 else if (row_mod == 1)
00506 colLabel += "Y";
00507 else if (row_mod == 2)
00508 colLabel += "Z";
00509 }
00510 else if (label.span == 1)
00511 {
00512 colLabel = label.text;
00513 }
00514 else
00515 colLabel = label.text + boost::lexical_cast<std::string>(row);
00516
00517 return colLabel;
00518 }
00519
00520
00523 template<typename T>
00524 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(std::istream& is)
00525 {
00526 typedef typename DataPoints::Label Label;
00527 typedef typename DataPoints::Labels Labels;
00528
00529 vector<T> xData;
00530 vector<T> yData;
00531 vector<T> zData;
00532 vector<T> padData;
00533 vector<string> header;
00534 vector<int> descColsToKeep;
00535
00536 map<int,DescAssociationPair> colToDescPair;
00537 map<string,int> descLabelToNumRows;
00538 map<string,int> descLabelToStartingRows;
00539
00540 vector<vector<T> > descCols;
00541 int numDescCols = 0;
00542 int dim(0);
00543 bool firstLine(true);
00544 bool hasHeader(false);
00545 Labels featureLabels, descriptorLabels;
00546 int xCol(-1);
00547 int yCol(-1);
00548 int zCol(-1);
00549
00550 char delimiters[] = " \t,;";
00551 char *token;
00552 while (!is.eof())
00553 {
00554 char line[1024];
00555 is.getline(line, sizeof(line));
00556 line[sizeof(line)-1] = 0;
00557
00558
00559 unsigned int len = strspn(line, " ,+-.1234567890Ee");
00560 if(len != strlen(line))
00561 {
00562
00563 hasHeader = true;
00564 }
00565 else
00566 {
00567 hasHeader = false;
00568 }
00569
00570
00571 if(firstLine)
00572 {
00573 char tmpLine[1024];
00574 char *brkt = 0;
00575 strcpy(tmpLine, line);
00576 token = strtok_r(tmpLine, delimiters, &brkt);
00577 while (token)
00578 {
00579 dim++;
00580
00581
00582 if(hasHeader)
00583 {
00584 header.push_back(string(token));
00585 }
00586
00587 token = strtok_r(NULL, delimiters, &brkt);
00588 }
00589
00590
00591 if (hasHeader)
00592 {
00593
00594 for(unsigned int i = 0; i < header.size(); i++)
00595 {
00596 std::string colLabel = header[i];
00597 if(colLabel.compare("x") == 0)
00598 xCol = i;
00599
00600 if(colLabel.compare("y") == 0)
00601 yCol = i;
00602
00603 if(colLabel.compare("z") == 0)
00604 zCol = i;
00605
00606 if(colLabelRegistered(colLabel))
00607 {
00608 descColsToKeep.push_back(i);
00609 DescAssociationPair associationPair = getDescAssociationPair(colLabel);
00610
00611 colToDescPair[i] = associationPair;
00612 descLabelToNumRows[associationPair.second]++;
00613 }
00614 }
00615
00616
00617
00618 int cumSum = 0;
00619 for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
00620 {
00621 descLabelToStartingRows[it->first] = cumSum;
00622 cumSum += it->second;
00623 }
00624
00625
00626 numDescCols = descColsToKeep.size();
00627 descCols.resize(numDescCols);
00628
00629 if(xCol == -1 || yCol == -1)
00630 {
00631 for(unsigned int i = 0; i < header.size(); i++)
00632 {
00633 cout << "(" << i << ") " << header[i] << endl;
00634 }
00635 cout << endl << "Enter ID for x: ";
00636 cin >> xCol;
00637 cout << "Enter ID for y: ";
00638 cin >> yCol;
00639 cout << "Enter ID for z (-1 if 2D data): ";
00640 cin >> zCol;
00641 }
00642 }
00643 else
00644 {
00645
00646 if (!(dim == 2 || dim == 3))
00647 {
00648 cout << "WARNING: " << dim << " columns detected. Not obvious which columns to load for x, y or z." << endl;
00649 cout << endl << "Enter column ID (starting from 0) for x: ";
00650 cin >> xCol;
00651 cout << "Enter column ID (starting from 0) for y: ";
00652 cin >> yCol;
00653 cout << "Enter column ID (starting from 0, -1 if 2D data) for z: ";
00654 cin >> zCol;
00655 }
00656 else
00657 {
00658
00659 xCol = 0;
00660 yCol = 1;
00661 if(dim == 3)
00662 zCol = 2;
00663 }
00664 }
00665
00666 if(zCol != -1)
00667 dim = 3;
00668 else
00669 dim = 2;
00670 }
00671
00672
00673 char *brkt = 0;
00674 token = strtok_r(line, delimiters, &brkt);
00675 int currentCol = 0;
00676 int d = 0;
00677 int nextDescCol = -1;
00678 if (numDescCols > 0)
00679 nextDescCol = descColsToKeep[0];
00680
00681 while (token)
00682 {
00683
00684 if(!hasHeader)
00685 {
00686 if(currentCol == xCol)
00687 xData.push_back(atof(token));
00688 if(currentCol == yCol)
00689 yData.push_back(atof(token));
00690 if(currentCol == zCol)
00691 zData.push_back(atof(token));
00692 if(currentCol == nextDescCol)
00693 {
00694 DescAssociationPair descPair = colToDescPair[nextDescCol];
00695 int startingRow = descLabelToStartingRows[descPair.second];
00696 descCols[startingRow + descPair.first].push_back(atof(token));
00697 d++;
00698
00699 if (d < numDescCols)
00700 nextDescCol = descColsToKeep[d];
00701 else
00702 nextDescCol = -1;
00703 }
00704 }
00705
00706 token = strtok_r(NULL, delimiters, &brkt);
00707 currentCol++;
00708 }
00709
00710
00711 padData.push_back(1);
00712
00713 if (firstLine)
00714 {
00715
00716 for (int i=0; i < dim; i++)
00717 {
00718 string text;
00719 text += char('x' + i);
00720 featureLabels.push_back(Label(text, 1));
00721 }
00722 featureLabels.push_back(Label("pad", 1));
00723
00724 for(map<string,int>::const_iterator d_it = descLabelToNumRows.begin(); d_it != descLabelToNumRows.end(); d_it++)
00725 {
00726 descriptorLabels.push_back(Label(d_it->first,d_it->second));
00727 }
00728 }
00729
00730 firstLine = false;
00731 }
00732
00733 assert(xData.size() == yData.size());
00734 int nbPoints = xData.size();
00735
00736
00737 Matrix features(dim+1, nbPoints);
00738 Matrix descriptors(numDescCols, nbPoints);
00739
00740 for(int i=0; i < nbPoints; i++)
00741 {
00742 features(0,i) = xData[i];
00743 features(1,i) = yData[i];
00744 if(dim == 3)
00745 {
00746 features(2,i) = zData[i];
00747 features(3,i) = 1;
00748 }
00749 else
00750 {
00751 features(2,i) = 1;
00752 }
00753
00754 for (int d = 0; d < numDescCols; d++)
00755 {
00756 descriptors(d,i) = descCols[d][i];
00757 }
00758 }
00759
00760 if (numDescCols > 0)
00761 {
00762 DataPoints dataPoints(features, featureLabels, descriptors, descriptorLabels);
00763 return dataPoints;
00764 }
00765 else
00766 {
00767 DataPoints dataPoints(features, featureLabels);
00768 return dataPoints;
00769 }
00770
00771
00772
00773
00774 }
00775
00776 template
00777 PointMatcher<float>::DataPoints PointMatcherIO<float>::loadCSV(const std::string& fileName);
00778 template
00779 PointMatcher<double>::DataPoints PointMatcherIO<double>::loadCSV(const std::string& fileName);
00780
00782 template<typename T>
00783 void PointMatcher<T>::DataPoints::save(const std::string& fileName) const
00784 {
00785 const boost::filesystem::path path(fileName);
00786 const string& ext(boost::filesystem::extension(path));
00787 if (boost::iequals(ext, ".vtk"))
00788 return PointMatcherIO<T>::saveVTK(*this, fileName);
00789 else if (boost::iequals(ext, ".csv"))
00790 return PointMatcherIO<T>::saveCSV(*this, fileName);
00791 else if (boost::iequals(ext, ".ply"))
00792 return PointMatcherIO<T>::savePLY(*this, fileName);
00793 else if (boost::iequals(ext, ".pcd"))
00794 return PointMatcherIO<T>::savePCD(*this, fileName);
00795 else
00796 throw runtime_error("saveAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
00797 }
00798
00799 template
00800 void PointMatcher<float>::DataPoints::save(const std::string& fileName) const;
00801 template
00802 void PointMatcher<double>::DataPoints::save(const std::string& fileName) const;
00803
00805 template<typename T>
00806 void PointMatcherIO<T>::saveCSV(const DataPoints& data, const std::string& fileName)
00807 {
00808 ofstream ofs(fileName.c_str());
00809 if (!ofs.good())
00810 throw runtime_error(string("Cannot open file ") + fileName);
00811 saveCSV(data, ofs);
00812 }
00813
00815 template<typename T>
00816 void PointMatcherIO<T>::saveCSV(const DataPoints& data, std::ostream& os)
00817 {
00818 const int pointCount(data.features.cols());
00819 const int dimCount(data.features.rows());
00820 const int descDimCount(data.descriptors.rows());
00821
00822 if (pointCount == 0)
00823 {
00824 cerr << "Warning, no points, doing nothing" << endl;
00825 return;
00826 }
00827
00828
00829 for (int i = 0; i < dimCount - 1; i++)
00830 {
00831 os << data.featureLabels[i].text;
00832
00833 if (!((i == (dimCount - 2)) && descDimCount == 0))
00834 os << ",";
00835 }
00836
00837 int n = 0;
00838 for (int i = 0; i < data.descriptorLabels.size(); i++)
00839 {
00840 Label lab = data.descriptorLabels[i];
00841 for (int s = 0; s < lab.span; s++)
00842 {
00843 os << getColLabel(lab,s);
00844 if (n != (descDimCount - 1))
00845 os << ",";
00846 n++;
00847 }
00848 }
00849
00850 os << "\n";
00851
00852
00853 for (int p = 0; p < pointCount; ++p)
00854 {
00855 for (int i = 0; i < dimCount-1; ++i)
00856 {
00857 os << data.features(i, p);
00858 if(!((i == (dimCount - 2)) && descDimCount == 0))
00859 os << " , ";
00860 }
00861
00862 for (int i = 0; i < descDimCount; i++)
00863 {
00864 os << data.descriptors(i,p);
00865 if (i != (descDimCount - 1))
00866 os << ",";
00867 }
00868 os << "\n";
00869 }
00870
00871 }
00872
00873 template
00874 void PointMatcherIO<float>::saveCSV(const DataPoints& data, const std::string& fileName);
00875 template
00876 void PointMatcherIO<double>::saveCSV(const DataPoints& data, const std::string& fileName);
00877
00879 template<typename T>
00880 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(const std::string& fileName)
00881 {
00882 ifstream ifs(fileName.c_str());
00883 if (!ifs.good())
00884 throw runtime_error(string("Cannot open file ") + fileName);
00885 return loadVTK(ifs);
00886 }
00887
00889 template<typename T>
00890 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(std::istream& is)
00891 {
00892
00893
00894
00895 DataPoints loadedPoints;
00896
00897
00898 string line;
00899 getline(is, line);
00900 if (line.find("# vtk DataFile Version") != 0)
00901 throw runtime_error(string("Wrong magic header, found ") + line);
00902 getline(is, line);
00903 getline(is, line);
00904 if (line != "ASCII")
00905 throw runtime_error(string("Wrong file type, expecting ASCII, found ") + line);
00906 getline(is, line);
00907
00908 SupportedVTKDataTypes dataType;
00909 if (line == "DATASET POLYDATA")
00910 dataType = POLYDATA;
00911 else if (line == "DATASET UNSTRUCTURED_GRID")
00912 dataType = UNSTRUCTURED_GRID;
00913 else
00914 throw runtime_error(string("Wrong data type, expecting DATASET POLYDATA, found ") + line);
00915
00916
00917
00918 string fieldName;
00919 string name;
00920 int pointCount = 0;
00921 string type;
00922 while (is.good())
00923 {
00924 is >> fieldName;
00925
00926
00927 if(fieldName == "POINTS")
00928 {
00929 is >> pointCount;
00930 is >> type;
00931
00932 if(!(type == "float" || type == "double"))
00933 throw runtime_error(string("Field POINTS can only be of type double or float"));
00934
00935 Matrix features(4, pointCount);
00936 for (int p = 0; p < pointCount; ++p)
00937 {
00938 is >> features(0, p);
00939 is >> features(1, p);
00940 is >> features(2, p);
00941 features(3, p) = 1.0;
00942 }
00943 loadedPoints.addFeature("x", features.row(0));
00944 loadedPoints.addFeature("y", features.row(1));
00945 loadedPoints.addFeature("z", features.row(2));
00946 loadedPoints.addFeature("pad", features.row(3));
00947 }
00948
00950
00951
00952 else if(dataType == POLYDATA && fieldName == "VERTICES")
00953 {
00954 int size;
00955 int verticeSize;
00956 is >> size >> verticeSize;
00957
00958 for (int p = 0; p < size; p++)
00959 {
00960 getline(is, line);
00961 if(line == "")
00962 p--;
00963 }
00964 }
00965
00966 else if(dataType == POLYDATA && fieldName == "LINES")
00967 {
00968 int size;
00969 int lineSize;
00970 is >> size >> lineSize;
00971
00972 for (int p = 0; p < size; p++)
00973 {
00974 getline(is, line);
00975 if(line == "")
00976 p--;
00977 }
00978 }
00979
00980 else if(dataType == POLYDATA && fieldName == "POLYGONS")
00981 {
00982 int size;
00983 int polySize;
00984 is >> size >> polySize;
00985
00986 for (int p = 0; p < size; p++)
00987 {
00988 getline(is, line);
00989 if(line == "")
00990 p--;
00991 }
00992 }
00993
00994 else if(dataType == POLYDATA && fieldName == "TRIANGLE_STRIPS")
00995 {
00996 int size;
00997 int stripSize;
00998 is >> size >> stripSize;
00999
01000 for (int p = 0; p < size; p++)
01001 {
01002 getline(is, line);
01003 if(line == "")
01004 p--;
01005 }
01006 }
01007
01008
01009 else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELLS")
01010 {
01011 int size;
01012 int cellSize;
01013 is >> size >> cellSize;
01014
01015 for (int p = 0; p < size; p++)
01016 {
01017 getline(is, line);
01018 if(line == "")
01019 p--;
01020 }
01021 }
01022 else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELL_TYPES")
01023 {
01024 int size;
01025 int cellSize;
01026 is >> size >> cellSize;
01027
01028 for (int p = 0; p < size; p++)
01029 {
01030 getline(is, line);
01031 if(line == "")
01032 p--;
01033 }
01034 }
01035
01037
01038 else if(fieldName == "POINT_DATA")
01039 {
01040 int descriptorCount;
01041 is >> descriptorCount;
01042 if(pointCount != descriptorCount)
01043 throw runtime_error(string("The size of POINTS is different than POINT_DATA"));
01044 }
01046
01047 else if (fieldName == "FIELD")
01048 {
01049 string fieldDataName;
01050 int fieldDataCount;
01051 is >> fieldDataName >> fieldDataCount;
01052
01053 for (int f = 0; f < fieldDataCount; f++)
01054 {
01055
01056 string fieldDataArrayName, fieldDataArrayType;
01057 int numComponents, numTuples;
01058 is >> fieldDataArrayName >> numComponents >> numTuples >> fieldDataArrayType;
01059
01060 int t_val;
01061 for (int t = 0; t < numComponents * numTuples; t++ )
01062 {
01063 is >> t_val;
01064 }
01065 }
01066 }
01067 else
01068 {
01069
01070 is >> name;
01071
01072 int dim = 0;
01073 bool skipLookupTable = false;
01074 if(fieldName == "SCALARS")
01075 {
01076 dim = 1;
01077 is >> type;
01078 skipLookupTable = true;
01079 }
01080 else if(fieldName == "VECTORS")
01081 {
01082 dim = 3;
01083 is >> type;
01084 }
01085 else if(fieldName == "TENSORS")
01086 {
01087 dim = 9;
01088 is >> type;
01089 }
01090 else if(fieldName == "NORMALS")
01091 {
01092 dim = 3;
01093 is >> type;
01094 }
01095 else if(fieldName == "COLOR_SCALARS")
01096 {
01097 is >> dim;
01098 type = "float";
01099 }
01100 else
01101 throw runtime_error(string("Unknown field name " + fieldName + ", expecting SCALARS, VECTORS, TENSORS, NORMALS or COLOR_SCALARS."));
01102
01103
01104 if(!(type == "float" || type == "double"))
01105 throw runtime_error(string("Field " + fieldName + " is " + type + " but can only be of type double or float"));
01106
01107
01108 if(skipLookupTable)
01109 {
01110
01111 getline(is, line);
01112 getline(is, line);
01113 }
01114
01115 Matrix descriptor(dim, pointCount);
01116 for (int p = 0; p < pointCount; ++p)
01117 {
01118 for(int d = 0; d < dim; d++)
01119 {
01120 is >> descriptor(d, p);
01121 }
01122 }
01123 loadedPoints.addDescriptor(name, descriptor);
01124 }
01125
01126 }
01127
01128 return loadedPoints;
01129 }
01130
01131 template
01132 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadVTK(const std::string& fileName);
01133 template
01134 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadVTK(const std::string& fileName);
01135
01136
01138 template<typename T>
01139 void PointMatcherIO<T>::saveVTK(const DataPoints& data, const std::string& fileName)
01140 {
01141 typedef typename InspectorsImpl<T>::VTKFileInspector VTKInspector;
01142
01143 Parametrizable::Parameters param;
01144 boost::assign::insert(param) ("baseFileName", "");
01145 VTKInspector vtkInspector(param);
01146 vtkInspector.dumpDataPoints(data, fileName);
01147 }
01148
01149
01150 template
01151 void PointMatcherIO<float>::saveVTK(const PointMatcherIO<float>::DataPoints& data, const std::string& fileName);
01152 template
01153 void PointMatcherIO<double>::saveVTK(const PointMatcher<double>::DataPoints& data, const std::string& fileName);
01154
01163 template<typename T>
01164 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPLY(const std::string& fileName)
01165 {
01166 ifstream ifs(fileName.c_str());
01167 if (!ifs.good())
01168 throw runtime_error(string("Cannot open file ") + fileName);
01169 return loadPLY(ifs);
01170 }
01171
01172 template
01173 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPLY(const string& fileName);
01174 template
01175 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPLY(const string& fileName);
01176
01179 template <typename T>
01180 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPLY(std::istream& is)
01181 {
01182 typedef typename DataPoints::Label Label;
01183 typedef typename DataPoints::Labels Labels;
01184 typedef vector<PLYElement*> Elements;
01185
01187
01188 bool format_defined = false;
01189 bool header_processed = false;
01190
01191 Elements elements;
01192 PLYElementF element_f;
01193 PLYElement* current_element = NULL;
01194 bool skip_props = false;
01195 unsigned elem_offset = 0;
01196 string line;
01197 getline(is, line);
01198
01199 if (line.find("ply") != 0) {
01200 throw runtime_error(string("PLY parse error: wrong magic header, found ") + line);
01201 }
01202
01203 while (!header_processed)
01204 {
01205 if(!getline(is, line))
01206 throw runtime_error("PLY parse error: reached end of file before end of header definition");
01207
01208 if ( line.empty() )
01209 continue;
01210 istringstream stringstream (line);
01211
01212 string keyword;
01213 stringstream >> keyword;
01214
01215
01216 if (keyword == "comment") {
01217 continue;
01218 }
01219
01220 if (keyword == "format")
01221 {
01222 if (format_defined)
01223 throw runtime_error("PLY parse error: format already defined");
01224
01225 string format_str, version_str;
01226 stringstream >> format_str >> version_str;
01227
01228 if (format_str != "ascii" && format_str != "binary_little_endian" && format_str != "binary_big_endian")
01229 throw runtime_error(string("PLY parse error: format ") + format_str + string(" is not supported"));
01230
01231 if (format_str == "binary_little_endian" || format_str == "binary_big_endian")
01232 throw runtime_error(string("PLY parse error: binary PLY files are not supported"));
01233 if (version_str != "1.0")
01234 {
01235 throw runtime_error(string("PLY parse error: version ") + version_str + string(" of ply is not supported"));
01236 }
01237
01238 format_defined = true;
01239
01240 }
01241 else if (keyword == "element")
01242 {
01243 if (current_element != NULL && current_element->getNumSupportedProperties() == 0)
01244 {
01245 LOG_WARNING_STREAM("PLY parse warning: no supported properties in element " << current_element->name);
01246 }
01247
01248 string elem_name, elem_num_s;
01249 stringstream >> elem_name >> elem_num_s;
01250
01251 unsigned elem_num;
01252 try
01253 {
01254 elem_num = boost::lexical_cast<unsigned>(elem_num_s);
01255 }
01256 catch (boost::bad_lexical_cast& e)
01257 {
01258 throw runtime_error(string("PLY parse error: bad number of elements ") + elem_num_s + string(" for element ") + elem_name);
01259 }
01260
01261 if (element_f.elementSupported(elem_name))
01262 {
01263
01264 PLYElement* elem = element_f.createElement(elem_name, elem_num, elem_offset);
01265 current_element = elem;
01266
01267
01268 for (typename Elements::const_iterator it = elements.begin(); it != elements.end(); it++ )
01269 {
01270 if (**it == *elem) {
01271 throw runtime_error(string("PLY parse error: element: ") + elem_name + string( "is already defined"));
01272 }
01273 }
01274 elements.push_back(elem);
01275 skip_props = false;
01276 }
01277 else
01278 {
01279 LOG_WARNING_STREAM("PLY parse warning: element " << elem_name << " not supported. Skipping.");
01280 skip_props = true;
01281 }
01282
01283 elem_offset += elem_num;
01284 }
01285 else if (keyword == "property")
01286 {
01287 if (current_element == NULL)
01288 {
01289 throw runtime_error("PLY parse error: property listed without defining an element");
01290 }
01291
01292 if (skip_props)
01293 continue;
01294
01295 string next, prop_type, prop_name;
01296 stringstream >> next;
01297
01298
01299 if (next == "list")
01300 {
01301 string prop_idx_type;
01302 stringstream >> prop_idx_type >> prop_type >> prop_name;
01303
01304 PLYProperty list_prop(prop_idx_type, prop_type, prop_name, current_element->total_props);
01305
01306 if (current_element->supportsProperty(list_prop))
01307 {
01308 current_element->addProperty(list_prop);
01309 }
01310 else
01311 {
01312 LOG_WARNING_STREAM("PLY parse error: element " << current_element->name
01313 << " does not support property " << prop_name << " " << prop_type );
01314 }
01315 }
01316
01317 else
01318 {
01319 prop_type = next;
01320 stringstream >> prop_name;
01321 PLYProperty prop(prop_type, prop_name, current_element->total_props);
01322
01323 if (current_element->supportsProperty(prop))
01324 {
01325 current_element->addProperty(prop);
01326 }
01327 else
01328 {
01329 LOG_WARNING_STREAM("PLY parse error: element " << current_element->name <<
01330 " does not support property " << prop_name << " " << prop_type );
01331 }
01332 }
01333
01334 current_element->total_props++;
01335 }
01336 else if (keyword == "end_header")
01337 {
01338 if (!format_defined)
01339 {
01340 throw runtime_error(string("PLY parse error: format not defined in header"));
01341 }
01342
01343 if (elements.size() == 0)
01344 {
01345 throw runtime_error(string("PLY parse error: no elements defined in header"));
01346 }
01347
01348 if (current_element != NULL && current_element->getNumSupportedProperties() == 0)
01349 {
01350 LOG_WARNING_STREAM("PLY parse warning: no supported properties in element " << current_element->name);
01351 }
01352
01353 header_processed = true;
01354 }
01355 }
01356
01358
01359 Matrix features, descriptors;
01360 Labels feat_labels, desc_labels;
01361
01362 int l = 0;
01363 for (typename Elements::const_iterator el_it = elements.begin(); el_it != elements.end(); el_it++)
01364 {
01365 vector<PLYProperty> feature_props = (*el_it)->getFeatureProps();
01366 vector<PLYProperty> descriptor_props = (*el_it)->getDescriptorProps();
01367 PLYDescPropMap descriptor_map = (*el_it)->getDescPropMap();
01368 const unsigned n_points = (*el_it)->num;
01369 const unsigned offset = (*el_it)->offset;
01370 const unsigned n_feat = (*el_it)->getNumFeatures();
01371 const unsigned n_desc = (*el_it)->getNumDescriptors();
01372 const unsigned n_dprop = (*el_it)->getNumDescProp();
01373 int feat_offset = features.rows();
01374 int desc_offset = descriptors.rows();
01375
01376
01377 std::map<std::string,int> descPropToRow;
01378
01379
01380 for (typename vector<PLYProperty>::const_iterator it = feature_props.begin(); it != feature_props.end(); it++ )
01381 {
01382 feat_labels.push_back(Label(it->name));
01383 }
01384
01385 int r = 0;
01386 for (typename PLYDescPropMap::const_iterator it = descriptor_map.begin(); it != descriptor_map.end(); it++ )
01387 {
01388 desc_labels.push_back(Label(it->first,it->second.size()));
01389
01390 BOOST_FOREACH(PLYProperty prop, it->second )
01391 {
01392 descPropToRow[prop.name] = r;
01393 r++;
01394 }
01395
01396 }
01397
01398
01399
01400
01401 features.conservativeResize(n_feat+feat_offset,n_points);
01402
01403 if (n_desc > 0)
01404 descriptors.conservativeResize(n_dprop+desc_offset,n_points);
01405
01406 while (l < (offset + n_points) )
01407 {
01408 if(!getline(is, line))
01409 throw runtime_error("PLY parse error: expected more data lines");
01410
01411 if ( line.empty() )
01412 continue;
01413
01414 istringstream ss (line);
01415
01416 string first_word, prop_s;
01417 ss >> first_word;
01418
01419
01420 if (first_word == "comment") {
01421 continue;
01422 }
01423
01424
01425 if (l < offset)
01426 {
01427 l++;
01428 continue;
01429 }
01430
01431 unsigned f = 0;
01432 unsigned d = 0;
01433
01434 for (int pr = 0; f < n_feat || d < n_dprop ; pr++)
01435 {
01436 unsigned next_f = feature_props[f].pos;
01437 int next_d, next_d_r;
01438 if (n_desc > 0)
01439 {
01440 next_d = descriptor_props[d].pos;
01441 next_d_r = descPropToRow[descriptor_props[d].name];
01442 }
01443 else
01444 next_d = -1;
01445
01446
01447 T prop_val;
01448
01449 if (pr > 0)
01450 {
01451 if(!(ss >> prop_val))
01452 throw runtime_error(string("PLY parse error: at line ") + boost::lexical_cast<string>(l));
01453 }
01454 else
01455 prop_val = boost::lexical_cast<T>(first_word);
01456
01457 if (pr == next_f)
01458 {
01459 features(f+feat_offset,l-offset) = prop_val;
01460 f++;
01461 }
01462
01463 else if (pr == next_d)
01464 {
01465 descriptors(next_d_r+desc_offset,l-offset) = prop_val;
01466 d++;
01467 }
01468 }
01469
01470 l++;
01471 }
01472 }
01473
01474
01475 feat_labels.push_back(Label("pad"));
01476 features.conservativeResize(features.rows()+1,features.cols());
01477 features.row(features.rows()-1) = Vector::Ones(features.cols());
01478
01479 if (descriptors.rows() > 0)
01480 {
01481 DataPoints loadedPoints(features,feat_labels,descriptors,desc_labels);
01482 return loadedPoints;
01483 }
01484 else
01485 {
01486 DataPoints loadedPoints(features,feat_labels);
01487 return loadedPoints;
01488 }
01489 }
01490
01491 template<typename T>
01492 void PointMatcherIO<T>::savePLY(const DataPoints& data,
01493 const std::string& fileName)
01494 {
01495 typedef typename DataPoints::Label Label;
01496
01497
01498 ofstream ofs(fileName.c_str());
01499 if (!ofs.good())
01500 throw runtime_error(string("Cannot open file ") + fileName);
01501
01502 const int pointCount(data.features.cols());
01503 const int featCount(data.features.rows());
01504 const int descRows(data.descriptors.rows());
01505
01506
01507 if (pointCount == 0)
01508 {
01509 cerr << "Warning, no points, doing nothing" << endl;
01510 return;
01511 }
01512
01513 ofs << "ply\n" <<"format ascii 1.0\n";
01514 ofs << "element vertex " << pointCount << "\n";
01515 for (int f=0; f <(featCount-1); f++)
01516 {
01517 ofs << "property float " << data.featureLabels[f].text << "\n";
01518 }
01519
01520 for (int i = 0; i < data.descriptorLabels.size(); i++)
01521 {
01522 Label lab = data.descriptorLabels[i];
01523 for (int s = 0; s < lab.span; s++)
01524 {
01525 ofs << "property float " << getColLabel(lab,s) << "\n";
01526 }
01527 }
01528
01529 ofs << "end_header\n";
01530
01531
01532 for (int p = 0; p < pointCount; ++p)
01533 {
01534 for (int f = 0; f < featCount - 1; ++f)
01535 {
01536 ofs << data.features(f, p);
01537 if(!(f == featCount-2 && descRows == 0))
01538 ofs << " ";
01539 }
01540 for (int d = 0; d < descRows; ++d)
01541 {
01542 ofs << data.descriptors(d, p);
01543 if(d != descRows-1)
01544 ofs << " ";
01545 }
01546 ofs << "\n";
01547 }
01548
01549 ofs.close();
01550 }
01551
01552 template
01553 void PointMatcherIO<float>::savePLY(const DataPoints& data, const std::string& fileName);
01554 template
01555 void PointMatcherIO<double>::savePLY(const DataPoints& data, const std::string& fileName);
01556
01558 template<typename T>
01559 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& type,
01560 const std::string& name, const unsigned pos, const bool is_feature) :
01561 name(name), type(type), pos(pos), is_feature(is_feature) {
01562 if (plyPropTypeValid(type))
01563 {
01564 is_list = false;
01565 }
01566 else
01567 {
01568 throw std::runtime_error(
01569 std::string("PLY parse error: property type ") + type
01570 + std::string(" for property ") + name
01571 + std::string(" is invalid"));
01572 }
01573 }
01574
01576 template<typename T>
01577 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& idx_type,
01578 const std::string& type, const std::string& name, const unsigned pos, const bool is_feature) :
01579 name(name), type(type), idx_type(idx_type), pos(pos), is_feature(is_feature)
01580 {
01581 if (plyPropTypeValid(idx_type) && plyPropTypeValid(type)) {
01582 is_list = true;
01583 } else
01584 throw std::runtime_error(
01585 std::string("PLY parse error: property list type ") + idx_type
01586 + std::string(" ") + type
01587 + std::string(" for property ") + name
01588 + std::string(" is invalid"));
01589 }
01590
01591 template
01592 class PointMatcherIO<float>::PLYElement;
01593 template
01594 class PointMatcherIO<double>::PLYElement;
01595
01596 template
01597 class PointMatcherIO<float>::PLYProperty;
01598 template
01599 class PointMatcherIO<double>::PLYProperty;
01600
01601 template <typename T>
01602 void PointMatcherIO<T>::PLYElement::addProperty(
01603 PLYProperty& prop) {
01604 if (supportsProperty(prop))
01605 {
01606
01607
01608 PMPropTypes pm_type = getPMType(prop);
01609
01610 if (pm_type == FEATURE)
01611 {
01612 features.push_back(prop);
01613 }
01614 else if (pm_type == DESCRIPTOR)
01615 {
01616 DescAssociationPair associationPair = getDescAssociationPair(prop.name);
01617
01618
01619
01620 std::vector<PLYProperty>* propList = &descriptor_map[associationPair.second];
01621
01622
01623 if (associationPair.first >= propList->size())
01624 {
01625 propList->resize(associationPair.first + 1);
01626 (*propList)[associationPair.first] = prop;
01627 }
01628
01629 else
01630 (*propList)[associationPair.first] = prop;
01631
01632 descriptors.push_back(prop);
01633 }
01634 else
01635 {
01636 throw std::runtime_error("PLY parse error: tried at add an unsupported property");
01637 }
01638 }
01639 else
01640 throw std::runtime_error(
01641 std::string("PLY parse error: property ") + prop.name
01642 + std::string(" not supported by element ") + name);
01643 }
01644
01645 template <typename T>
01646 int PointMatcherIO<T>::PLYElement::getNumFeatures() const
01647 {
01648 return features.size();
01649 }
01650
01651 template <typename T>
01652 int PointMatcherIO<T>::PLYElement::getNumDescriptors() const
01653 {
01654 return descriptor_map.size();
01655 }
01656
01657 template <typename T>
01658 int PointMatcherIO<T>::PLYElement::getNumDescProp() const
01659 {
01660 return descriptors.size();
01661 }
01662
01663 template <typename T>
01664 const std::vector<typename PointMatcherIO<T>::PLYProperty>& PointMatcherIO<T>::PLYElement::getFeatureProps() const
01665 {
01666 return features;
01667 }
01668
01669 template <typename T>
01670 const std::vector<typename PointMatcherIO<T>::PLYProperty>& PointMatcherIO<T>::PLYElement::getDescriptorProps() const
01671 {
01672 return descriptors;
01673 }
01674
01675 template <typename T>
01676 const typename PointMatcherIO<T>::PLYDescPropMap& PointMatcherIO<T>::PLYElement::getDescPropMap() const
01677 {
01678 return descriptor_map;
01679 }
01680
01681 template <typename T>
01682 size_t PointMatcherIO<T>::PLYElement::getNumSupportedProperties() const {
01683 return features.size() + descriptors.size() ;
01684 }
01685
01686 template <typename T>
01687 bool PointMatcherIO<T>::PLYElement::supportsProperty(const PLYProperty& prop) const
01688 {
01689 return getPMType(prop) != UNSUPPORTED;
01690 }
01691
01692 template<typename T>
01693 typename PointMatcherIO<T>::PLYElement::PMPropTypes PointMatcherIO<T>::PLYVertex::getPMType(const PLYProperty& prop) const
01694 {
01695 if (prop.name == "x" || prop.name == "y" || prop.name == "z")
01696 return this->FEATURE;
01697 else if (colLabelRegistered(prop.name))
01698 return this->DESCRIPTOR;
01699 else
01700 return this->UNSUPPORTED;
01701 }
01702
01703 template <typename T>
01704 typename PointMatcherIO<T>::PLYElementF::ElementTypes PointMatcherIO<T>::PLYElementF::getElementType(const std::string& elem_name)
01705 {
01706 string lc = elem_name;
01707 boost::algorithm::to_lower(lc);
01708 if (lc == "vertex")
01709 {
01710 return VERTEX;
01711 }
01712 else
01713 {
01714 return UNSUPPORTED;
01715 }
01716 }
01717
01718 template <typename T>
01719 bool PointMatcherIO<T>::PLYElementF::elementSupported(const std::string& elem_name)
01720 {
01721 return getElementType(elem_name) != UNSUPPORTED;
01722 }
01723
01724 template<typename T>
01725 typename PointMatcherIO<T>::PLYElement* PointMatcherIO<T>::PLYElementF::createElement(
01726 const std::string& elem_name, const int elem_num, const unsigned offset) {
01727 ElementTypes type = getElementType(elem_name);
01728 if (type == VERTEX)
01729 return new PLYVertex(elem_num, offset);
01730 else
01731 return NULL;
01732 }
01733
01734 template<typename T>
01735 bool PointMatcherIO<T>::plyPropTypeValid(const std::string& type) {
01736 return (type == "char" || type == "uchar" || type == "short"
01737 || type == "ushort" || type == "int" || type == "uint"
01738 || type == "float" || type == "double");
01739 }
01740
01741 template <typename T>
01742 bool PointMatcherIO<T>::PLYElement::operator==(const PLYElement& rhs) const
01743 {
01744 return name == rhs.name;
01745 }
01746
01747
01748 template <typename T>
01749 bool PointMatcherIO<T>::PLYProperty::operator==(const PLYProperty& rhs) const
01750 {
01751 return name == rhs.name && type == rhs.type;
01752 }
01753
01756 template<typename T>
01757 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(const string& fileName) {
01758 ifstream ifs(fileName.c_str());
01759 if (!ifs.good())
01760 throw runtime_error(string("Cannot open file ") + fileName);
01761 return loadPCD(ifs);
01762 }
01763
01764 template
01765 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(const string& fileName);
01766 template
01767 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(const string& fileName);
01768
01769
01770
01771
01772
01773
01776 template<typename T>
01777 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(std::istream& is) {
01778
01779 typedef typename DataPoints::Label Label;
01780 typedef typename DataPoints::Labels Labels;
01781
01782 size_t numFields = 0;
01783 size_t numDataFields = 0;
01784 int xFieldCol = -1;
01785 int yFieldCol = -1;
01786 int zFieldCol = -1;
01787
01788 vector<int> descFieldsToKeep;
01789 map<int,DescAssociationPair> colToDescPair;
01790 map<string,int> descLabelToNumRows;
01791 map<string,int> descLabelToStartingRows;
01792 vector<int> descDimensions;
01793
01794 string xFieldType;
01795 string yFieldType;
01796 string zFieldType;
01797
01798 size_t width;
01799 size_t height;
01800 size_t numPoints;
01801 size_t numPointsR;
01802
01803 size_t lineNum = 0;
01804
01805 while (!is.eof())
01806 {
01807 string line;
01808 getline(is, line);
01809
01810
01811 boost::trim (line);
01812
01813
01814 if (line.substr(0,1) == "#")
01815 {
01816 lineNum++;
01817 continue;
01818 }
01819
01820 vector<string> tokens;
01821 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
01822
01823 string pcd_version_str;
01824 if (tokens[0] == "VERSION")
01825 {
01826 if (tokens[1] != "0.7" && tokens[1] != ".7")
01827 throw runtime_error("PCD Parse Error: Only PCD Version 0.7 is supported");
01828 }
01829
01830 else if (tokens[0] == "FIELDS")
01831 {
01832 numFields = tokens.size() - 1;
01833 numDataFields = numFields;
01834 for (size_t i = 1; i < tokens.size(); i++)
01835 {
01836 if (tokens[i] == "x")
01837 xFieldCol = i - 1;
01838 else if (tokens[i] == "y")
01839 yFieldCol = i - 1;
01840 else if (tokens[i] == "z")
01841 zFieldCol = i - 1;
01842
01843 else if(colLabelRegistered(tokens[i]))
01844 {
01845 descFieldsToKeep.push_back(i);
01846 DescAssociationPair associationPair = getDescAssociationPair(tokens[i]);
01847
01848 colToDescPair[i] = associationPair;
01849 descLabelToNumRows[associationPair.second]++;
01850 }
01851 }
01852 }
01853
01854 else if (tokens[0] == "SIZE")
01855 {
01856 if (xFieldCol == -1 || yFieldCol == -1)
01857 throw runtime_error("PCD Parse Error: x field or y field not defined");
01858 if (tokens.size() - 1 != numFields)
01859 throw runtime_error("PCD Parse Error: size not defined for all fields");
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872 }
01873
01874 else if (tokens[0] == "TYPE")
01875 {
01876 if (xFieldCol == -1 || yFieldCol == -1)
01877 throw runtime_error("PCD Parse Error: x field or y field not defined");
01878 if (tokens.size() - 1 != numFields)
01879 throw runtime_error("PCD Parse Error: type not defined for all fields");
01880 xFieldType = tokens[xFieldCol + 1];
01881 yFieldType = tokens[yFieldCol + 1];
01882
01883 if (xFieldType != "I" && xFieldType != "U" && xFieldType != "F" &&
01884 yFieldType != "I" && yFieldType != "U" && yFieldType != "F")
01885 throw runtime_error("PCD Parse Error: invalid type");
01886
01887 if (zFieldCol > -1)
01888 {
01889 zFieldType = tokens[zFieldCol + 1];
01890 if (zFieldType != "I" && zFieldType != "U" && zFieldType != "F")
01891 throw runtime_error("PCD Parse Error: invalid type");
01892 }
01893 }
01894
01895
01896 else if (tokens[0] == "COUNT")
01897 {
01898 if (tokens.size() - 1 != numFields)
01899 throw runtime_error("PCD Parse Error: COUNT number does not match number of fields");
01900
01901
01902 numDataFields = 0;
01903
01904
01905
01906 map<int, DescAssociationPair> colToDescPair_ = colToDescPair;
01907 colToDescPair.clear();
01908
01909 vector<int>::const_iterator nextFieldToKeepIt = descFieldsToKeep.begin();
01910 for (int i = 1; i < tokens.size(); i++)
01911 {
01912 int count = boost::lexical_cast<int>(tokens[i]);
01913
01914 if (i == *nextFieldToKeepIt)
01915 {
01916 string descLabel = colToDescPair_[i].second;
01917 descLabelToNumRows[descLabel] = count;
01918
01919 for (int p = 0; p < count; p++)
01920 colToDescPair[numDataFields + p] = DescAssociationPair(p, descLabel);
01921
01922 if (nextFieldToKeepIt != descFieldsToKeep.end())
01923 nextFieldToKeepIt++;
01924 }
01925
01926 numDataFields += count;
01927
01928 }
01929 }
01930
01931 else if (tokens[0] == "WIDTH")
01932 {
01933 try
01934 {
01935 width = boost::lexical_cast<int>(tokens[1]);
01936 } catch (boost::bad_lexical_cast& e)
01937 {
01938 throw runtime_error("PCD Parse Error: invalid width");
01939 }
01940 }
01941
01942 else if (tokens[0] == "HEIGHT")
01943 {
01944 try
01945 {
01946 height = boost::lexical_cast<int>(tokens[1]);
01947 } catch (boost::bad_lexical_cast& e)
01948 {
01949 throw runtime_error("PCD Parse Error: invalid width");
01950 }
01951 }
01952
01953
01954 else if (tokens[0] == "VIEWPOINT")
01955 {
01956 continue;
01957 }
01958
01959 else if (tokens[0] == "POINTS")
01960 {
01961 try
01962 {
01963 numPointsR = boost::lexical_cast<int>(tokens[1]);
01964 }
01965 catch (boost::bad_lexical_cast& e)
01966 {
01967 throw runtime_error("PCD Parse Error: invalid number of points");
01968 }
01969 }
01970
01971 else if (tokens[0] == "DATA")
01972 {
01973 if (tokens[1] != "ascii")
01974 throw runtime_error("PCD Parse Error: only ascii data is supported");
01975
01976 break;
01977 }
01978
01979 lineNum++;
01980 }
01981
01982
01983 numPoints = width * height;
01984
01985 if (numPoints != numPointsR)
01986 throw runtime_error("PCD Parse Error: POINTS field does not match WIDTH and HEIGHT fields");
01987
01988
01989 Matrix features;
01990 if (zFieldCol > -1)
01991 features = Matrix(4,numPoints);
01992 else
01993 features = Matrix(3,numPoints);
01994
01995
01996
01997
01998 int cumSum = 0;
01999 for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02000 {
02001 descLabelToStartingRows[it->first] = cumSum;
02002 cumSum += it->second;
02003 }
02004
02005
02006 size_t numDescCols = cumSum;
02007 Matrix descriptors(numDescCols,numPoints);
02008
02009
02010 size_t p = 0;
02011 while (!is.eof())
02012 {
02013 string line;
02014 getline(is, line);
02015
02016
02017 boost::trim (line);
02018
02019
02020 if (line.substr(0,1) == "#")
02021 {
02022 lineNum++;
02023 continue;
02024 }
02025
02026 vector<string> tokens;
02027 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
02028
02029 if (tokens.size() != numDataFields)
02030 throw runtime_error(string("PCD Parse Error: number of data columns does not match number of fields at line: ") + boost::lexical_cast<string>(lineNum));
02031
02032 features(0,p) = boost::lexical_cast<T>(tokens[xFieldCol]);
02033 features(1,p) = boost::lexical_cast<T>(tokens[yFieldCol]);
02034
02035 if (zFieldCol > -1)
02036 {
02037 features(2,p) = boost::lexical_cast<float>(tokens[zFieldCol]);
02038 features(3,p) = 1;
02039 } else
02040 features(2,p) = 1;
02041
02042 for (map<int,DescAssociationPair>::const_iterator cit = colToDescPair.begin();
02043 cit != colToDescPair.end(); cit++)
02044 {
02045 int startingRow = descLabelToStartingRows[cit->second.second];
02046 descriptors(startingRow + cit->second.first,p) = boost::lexical_cast<T>(tokens[cit->first]);
02047 }
02048
02049 p++;
02050 lineNum++;
02051
02052 if (p == numPoints)
02053 break;
02054
02055 }
02056
02057 if (p != numPoints)
02058 {
02059 boost::format errorFmt("PCD Parse Error: the number of points in the data %1 is less than the specified number of points %2");
02060 errorFmt % p % numPoints;
02061 throw runtime_error(errorFmt.str());
02062 }
02063
02064 Labels featureLabels;
02065 featureLabels.push_back(Label("x"));
02066 featureLabels.push_back(Label("y"));
02067
02068 Labels descriptorLabels;
02069 int n = 0;
02070 for (map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02071 {
02072 descriptorLabels.push_back(Label(it->first,it->second));
02073 n++;
02074 }
02075
02076 if (zFieldCol > -1)
02077 featureLabels.push_back(Label("z"));
02078
02079 DataPoints out;
02080
02081 if (numDescCols > 0)
02082 out = DataPoints(features, featureLabels, descriptors, descriptorLabels);
02083 else
02084 out = DataPoints(features, featureLabels);
02085 return out;
02086 }
02087
02088 template<typename T>
02089 void PointMatcherIO<T>::savePCD(const DataPoints& data,
02090 const std::string& fileName) {
02091 typedef typename DataPoints::Label Label;
02092
02093
02094 ofstream ofs(fileName.c_str());
02095 if (!ofs.good())
02096 throw runtime_error(string("Cannot open file ") + fileName);
02097
02098 const int pointCount(data.features.cols());
02099 const int featCount(data.features.rows());
02100 const int descRows(data.descriptors.rows());
02101 const int descCount(data.descriptorLabels.size());
02102
02103 if (pointCount == 0)
02104 {
02105 cerr << "Warning, no points, doing nothing" << endl;
02106 return;
02107 }
02108
02109 ofs << "# .PCD v.7 - Point Cloud Data file format\n" <<"VERSION .7\n";
02110 ofs << "FIELDS";
02111
02112 for (int f=0; f < (featCount - 1); f++)
02113 {
02114 ofs << " " << data.featureLabels[f].text;
02115 }
02116
02117 if (descRows == 0)
02118 ofs << "\n";
02119 else
02120 {
02121 for (int i = 0; i < descCount; i++)
02122 {
02123 ofs << " " << data.descriptorLabels[i].text;
02124 }
02125 ofs << "\n";
02126 }
02127
02128 ofs << "SIZE";
02129 for (int i =0; i < featCount - 1 + descCount; i++)
02130 {
02131 ofs << " 4";
02132 }
02133 ofs << "\n";
02134
02135 ofs << "TYPE";
02136 for (int i =0; i < featCount - 1 + descCount; i++)
02137 {
02138 ofs << " F";
02139 }
02140 ofs << "\n";
02141
02142 ofs << "COUNT";
02143 for (int f = 0; f < featCount - 1 ; f++ )
02144 ofs << " 1";
02145 if (descCount == 0)
02146 ofs << "\n";
02147 else
02148 {
02149 for (int i = 0; i < descCount; i++)
02150 {
02151 ofs << " " << data.descriptorLabels[i].span;
02152 }
02153 ofs << "\n";
02154 }
02155
02156 ofs << "WIDTH " << pointCount << "\n";
02157 ofs << "HEIGHT 1\n";
02158 ofs << "POINTS " << pointCount << "\n";
02159 ofs << "DATA ascii\n";
02160
02161
02162 for (int p = 0; p < pointCount; ++p)
02163 {
02164 for (int f = 0; f < featCount - 1; ++f)
02165 {
02166 ofs << data.features(f, p);
02167 if(!(f == featCount-2 && descRows == 0))
02168 ofs << " ";
02169 }
02170 for (int d = 0; d < descRows; ++d)
02171 {
02172 ofs << data.descriptors(d, p);
02173 if(d != descRows-1)
02174 ofs << " ";
02175 }
02176 ofs << "\n";
02177 }
02178
02179 ofs.close();
02180 }
02181
02182 template
02183 void PointMatcherIO<float>::savePCD(const DataPoints& data, const std::string& fileName);
02184 template
02185 void PointMatcherIO<double>::savePCD(const DataPoints& data, const std::string& fileName);
02186