IO.cpp
Go to the documentation of this file.
00001 // kate: replace-tabs off; indent-width 4; indent-mode normal
00002 // vim: ts=4:sw=4:noexpandtab
00003 /*
00004 
00005 Copyright (c) 2010--2012,
00006 François Pomerleau and Stephane Magnenat, ASL, ETHZ, Switzerland
00007 You can contact the authors at <f dot pomerleau at gmail dot com> and
00008 <stephane at magnenat dot net>
00009 
00010 All rights reserved.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014     * Redistributions of source code must retain the above copyright
00015       notice, this list of conditions and the following disclaimer.
00016     * Redistributions in binary form must reproduce the above copyright
00017       notice, this list of conditions and the following disclaimer in the
00018       documentation and/or other materials provided with the distribution.
00019     * Neither the name of the <organization> nor the
00020       names of its contributors may be used to endorse or promote products
00021       derived from this software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00024 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00025 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00026 DISCLAIMED. IN NO EVENT SHALL ETH-ASL BE LIABLE FOR ANY
00027 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00028 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
00030 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00031 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 #include "IO.h"
00037 #include "InspectorsImpl.h"
00038 
00039 // For logging
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 // Tokenize a string, excepted if it begins with a '%' (a comment in CSV)
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] != '%') // Jump line if it's commented
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 // Open and parse a CSV file, return the data
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 // load the rest of the file
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         // Use for debug
00140         
00141         //for(BOOST_AUTO(it,data.begin()); it!=data.end(); it++)
00142         //{
00143         //      cout << "--------------------------" << endl;
00144         //      cout << "Header: |" << (*it).first << "|" << endl;
00145         //      //for(unsigned i=0; i<(*it).second.size(); i++)
00146         //      //{
00147         //      //      cout << (*it).second[i] << endl;
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 // Empty constructor
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         // Look for transformations
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         // Check for consistency
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         // Load reading
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         // for every lines
00250         for(unsigned line=0; line<lineCount; line++)
00251         {
00252                 FileInfo info;
00253                 
00254                 // Files
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                 // Load transformations
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                 // Build the list
00272                 this->push_back(info);
00273         }
00274         
00275         // Debug: Print the list
00276         /*for(unsigned i=0; i<list.size(); i++)
00277         {
00278                 cout << "\n--------------------------" << endl;
00279                 cout << "Sequence " << i << ":" << endl;
00280                 cout << "Reading path: " << list[i].readingFileName << endl;
00281                 cout << "Reference path: " << list[i].referenceFileName << endl;
00282                 cout << "Extension: " << list[i].fileExtension << endl;
00283                 cout << "Tranformation:\n" << list[i].initialTransformation << endl;
00284                 cout << "Grativity:\n" << list[i].gravity << endl;
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         //TODO: add time here
00497 }
00498 
00499 // Class LabelGenerator
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 // Class LabelGenerator
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                 // format: eigVectors<0-2><X-Z>
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         //typedef typename DataPoints::Label Label;
00604         //typedef typename DataPoints::Labels Labels;
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; // Record of which columns will be read into a descriptor
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                 //char line[1024];
00632       string line;
00633       safeGetLine(is, line);
00634                 //is.getline(line, sizeof(line));
00635                 //line[sizeof(line)-1] = 0;
00636         
00637                 // Look for text header
00638                 unsigned int len = strspn(line.c_str(), " ,+-.1234567890Ee");
00639                 if(len != line.length())
00640                 {
00641                         //cout << "Header detected" << endl;
00642                         hasHeader = true;
00643                 }
00644                 else
00645                 {
00646                         hasHeader = false;
00647                 }
00648 
00649                 // Count dimension using first line
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                                 // Load text header
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                                 // Search for x, y and z tags
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                                 // Do cumulative sum over number of descriptor rows per decriptor to get the starting
00697                                 // index row of reach descriptor
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                                 // allocate descriptor vectors
00706                                 numDescCols = descColsToKeep.size(); // number of descriptor vectors
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                                 // Check if it is a simple file with only coordinates
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                                         // Assume logical order...
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                 // Load data!
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; // descriptor vector iterator
00759                 int nextDescCol = -1; // next descriptor column to be recorded
00760                 if (numDescCols > 0)
00761                         nextDescCol = descColsToKeep[0];
00762 
00763                 while (token)
00764                 {
00765                         // Load data only if no text is on the line
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                                         // check for next descriptor column, if there are no more than we will no longer check
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                 // Add one for uniform coordinates
00793                 padData.push_back(1);
00794                 
00795                 if (firstLine)
00796                 {
00797                         // create standard labels
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         // Transfer loaded points in specific structure (eigen matrix)
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         //cout << "Loaded " << dataPoints.features.cols() << " points." << endl;
00853         //cout << "Find " << dataPoints.features.rows() << " dimensions." << endl;
00854         //cout << features << endl;
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         // write header
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         // write points
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         //typedef typename DataPoints::Label Label;
00975         //typedef typename DataPoints::Labels Labels;
00976         
00977         DataPoints loadedPoints;
00978 
00979         // parse header
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         // parse points and descriptors
01000         string fieldName;
01001         string name;
01002         int pointCount = 0;
01003         string type;
01004         while (is.good())
01005         {
01006                 is >> fieldName;
01007                 
01008                 // load features
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                 // Dataset type
01033                 // POLYDATA
01034                 else if(dataType == POLYDATA && fieldName == "VERTICES")
01035                 {
01036                         int size;
01037                         int verticeSize;
01038                         is >> size >> verticeSize;
01039                         // Skip vertice definition
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                         // Skip line definition
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                         // Skip line definition
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                         // Skip line definition
01082                         for (int p = 0; p < size; p++)
01083                         {
01084                                 getline(is, line);
01085                                 if(line == "")
01086                                         p--;
01087                         }
01088                 }
01089 
01090                 // Unstructure Grid
01091                 else if(dataType == UNSTRUCTURED_GRID && fieldName == "CELLS")
01092                 {
01093                         int size;
01094                         int cellSize;
01095                         is >> size >> cellSize;
01096                         // Skip line definition
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                         // Skip line definition
01110                         for (int p = 0; p < size; p++)
01111                         {
01112                                 getline(is, line);
01113                                 if(line == "")
01114                                         p--;
01115                         }
01116                 }
01117 
01119                 // Point data
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                 // Field data is ignored
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                                 //getline(is, line);
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 // Load descriptors
01150                 {
01151                         // descriptor name
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                         // Skip LOOKUP_TABLE line
01190                         if(skipLookupTable)
01191                         {
01192                                 //FIXME: FP - why the first line is aways empty?
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         //typedef typename DataPoints::Label Label;
01265         //typedef typename DataPoints::Labels Labels;
01266         typedef vector<PLYElement*> Elements;
01267 
01268         /*
01269         Steps:
01270         1- PARSE PLY HEADER
01271         2- ASSIGN PLY PROPERTIES TO DATAPOINTS ROWS
01272         3- Reserve memory for a DataPoints
01273         4- Parse PLY vertex to appropriate DataPoints cols and rows 
01274         5- Assemble final DataPoints
01275 
01276         PLY organisation:
01277 
01278         element 1 [name, size]
01279                 - property 1 [type, name]
01280                 - property 2
01281                 - ...
01282         element 2
01283                 -...
01284 
01285         */
01286 
01287 
01289         // 1- PARSE PLY HEADER
01290         bool format_defined = false;
01291         bool header_processed = false;
01292 
01293         Elements elements;
01294         PLYElementF element_f; // factory
01295         PLYElement* current_element = NULL;
01296         bool skip_props = false; // flag to skip properties if element is not supported
01297         unsigned elem_offset = 0; // keep track of line position of elements that are supported
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                 // ignore comment
01319                 if (keyword == "comment") {
01320                         continue;
01321                 }
01322 
01323                 // We only support ascii and ply version 1.0
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                                 // Initialize current element
01365                                 PLYElement* elem = element_f.createElement(elem_name, elem_num, elem_offset);
01366                                 current_element = elem;
01367 
01368                                 // check that element is not already defined
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                         // PLY list property
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                         // PLY regular property
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         // 2- ASSIGN PLY PROPERTIES TO DATAPOINTS ROWS
01439         
01440         // Fetch vertex
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         // Known features and descriptors
01449         const SupportedLabels externalLabels = getSupportedExternalLabels();
01450         
01451         int rowIdFeatures = 0;
01452         int rowIdDescriptors = 0;
01453         
01454         LabelGenerator featLabelGen, descLabelGen;
01455         
01456         // Loop through all known external names (ordered list)
01457         for(size_t i=0; i<externalLabels.size(); i++)
01458         {
01459                 const SupportedLabel supLabel = externalLabels[i];
01460 
01461                 //Search if that feature exist
01462                 for(it_PLYProp it=vertex->properties.begin(); it!=vertex->properties.end(); ++it)
01463                 {
01464                         if(supLabel.externalName == it->name)
01465                         {
01466                                 // Assign rowId in that order
01467                                 if(supLabel.type == FEATURE)
01468                                 {
01469                                         it->pmRowID = rowIdFeatures;
01470 
01471                                         // Prepare feature labels
01472                                         featLabelGen.add(supLabel.internalName);
01473 
01474                                         rowIdFeatures++;
01475                                 }
01476                                 else if (supLabel.type == DESCRIPTOR)
01477                                 {
01478                                         it->pmRowID = rowIdDescriptors;
01479 
01480                                         // Prepare descriptor labels
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                 //TODO: Handle random descriptor names
01494         }
01495 
01497         // 3- RESERVE DATAPOINTS MEMORY
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         // 4- PARSE PLY DATA (vertex)
01510         const int nbProp = vertex->total_props;
01511         const int nbValues = nbPoints*nbProp;
01512         int propID = 0;
01513         int col = 0;
01514         for(int i=0; i<nbValues; i++)
01515         {
01516                 T value;
01517                 if(!(is >> value))
01518                 {
01519                         throw runtime_error(
01520                         (boost::format("PLY parse error: expected %1% values (%2% points with %3% properties) but only found %4% values.") % nbValues % nbPoints % nbProp % i).str());
01521                 }
01522                 else
01523                 {
01524                         const int row = vertex->properties[propID].pmRowID;
01525                         const PMPropTypes type = vertex->properties[propID].pmType;
01526                         
01527                         if(type == FEATURE)
01528                         {
01529                                 features(row, col) = value;
01530                         }
01531                         else if(type == DESCRIPTOR)
01532                         {
01533                                 descriptors(row, col) = value;
01534                         }
01535 
01536                         ++propID;
01537 
01538                         if(propID >= nbProp)
01539                         {
01540                                 propID = 0;
01541                                 ++col;
01542                         }
01543                 }
01544         }
01545 
01546 
01547 
01549         // 5- ASSEMBLE FINAL DATAPOINTS
01550         
01551         DataPoints loadedPoints;
01552 
01553         if (descriptors.rows() > 0)
01554         {
01555                 loadedPoints = DataPoints(features, featLabelGen.getLabels(), 
01556                                           descriptors,descLabelGen.getLabels());
01557         }
01558         else
01559         {
01560                 DataPoints loadedPoints(features, featLabelGen.getLabels());
01561         }
01562 
01563         // Ensure homogeous coordinates
01564         if(!loadedPoints.featureExists("pad"))
01565         {
01566                 loadedPoints.addFeature("pad", Matrix::Ones(1,nbPoints));
01567         }
01568 
01569         return loadedPoints;
01570 
01571 }
01572 
01573 template<typename T>
01574 void PointMatcherIO<T>::savePLY(const DataPoints& data,
01575                 const std::string& fileName)
01576 {
01577         //typedef typename DataPoints::Labels Labels;
01578 
01579         ofstream ofs(fileName.c_str());
01580         if (!ofs.good())
01581                 throw runtime_error(string("Cannot open file ") + fileName);
01582 
01583         const int pointCount(data.features.cols());
01584         const int featCount(data.features.rows());
01585         const int descRows(data.descriptors.rows());
01586 
01587 
01588         if (pointCount == 0)
01589         {
01590                 cerr << "Warning, no points, doing nothing" << endl;
01591                 return;
01592         }
01593 
01594         ofs << "ply\n" <<"format ascii 1.0\n";
01595         ofs << "element vertex " << pointCount << "\n";
01596         for (int f=0; f <(featCount-1); f++)
01597         {
01598                 ofs << "property float " << data.featureLabels[f].text << "\n";
01599         }
01600 
01601         for (size_t i = 0; i < data.descriptorLabels.size(); i++)
01602         {
01603                 Label lab = data.descriptorLabels[i];
01604                 for (size_t s = 0; s < lab.span; s++)
01605                 {
01606                         ofs << "property float " << getColLabel(lab,s) << "\n";
01607                 }
01608         }
01609 
01610         ofs << "end_header\n";
01611 
01612         // write points
01613         for (int p = 0; p < pointCount; ++p)
01614         {
01615                 for (int f = 0; f < featCount - 1; ++f)
01616                 {
01617                         ofs << data.features(f, p);
01618                         if(!(f == featCount-2 && descRows == 0))
01619                                 ofs << " ";
01620                 }
01621                 for (int d = 0; d < descRows; ++d)
01622                 {
01623                         ofs << data.descriptors(d, p);
01624                         if(d != descRows-1)
01625                                 ofs << " ";
01626                 }
01627                 ofs << "\n";
01628         }
01629 
01630         ofs.close();
01631 }
01632 
01633 template
01634 void PointMatcherIO<float>::savePLY(const DataPoints& data, const std::string& fileName);
01635 template
01636 void PointMatcherIO<double>::savePLY(const DataPoints& data, const std::string& fileName);
01637 
01639 template<typename T>
01640 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& type,
01641                 const std::string& name, const unsigned pos, const bool is_feature) :
01642                 name(name), 
01643                 type(type), 
01644                 pos(pos), 
01645                 is_feature(is_feature)  
01646 {
01647         if (plyPropTypeValid(type))
01648         {
01649                 is_list = false;
01650         }
01651         else
01652         {
01653                 throw std::runtime_error(
01654                                 std::string("PLY parse error: property type ") + type
01655                                                 + std::string(" for property ") + name
01656                                                 + std::string(" is invalid"));
01657         }
01658 
01659         pmType = getPMType(name);
01660         pmRowID = -1;
01661 
01662 }
01663 
01665 template<typename T>
01666 PointMatcherIO<T>::PLYProperty::PLYProperty(const std::string& idx_type,
01667                 const std::string& type, const std::string& name, const unsigned pos, const bool is_feature) :
01668                 name(name), 
01669                 type(type), 
01670                 idx_type(idx_type), 
01671                 pos(pos), 
01672                 is_feature(is_feature)
01673 {
01674         if (plyPropTypeValid(idx_type) && plyPropTypeValid(type)) 
01675         {
01676                 is_list = true;
01677         } 
01678         else
01679         {
01680                 throw std::runtime_error(
01681                                 std::string("PLY parse error: property list type ") + idx_type
01682                                                 + std::string(" ") + type
01683                                                 + std::string(" for property ") + name
01684                                                 + std::string(" is invalid"));
01685         }
01686 
01687         pmType = getPMType(name);
01688         pmRowID = -1;
01689 }
01690 
01691 template
01692 class PointMatcherIO<float>::PLYElement;
01693 template
01694 class PointMatcherIO<double>::PLYElement;
01695 
01696 template
01697 class PointMatcherIO<float>::PLYProperty;
01698 template
01699 class PointMatcherIO<double>::PLYProperty;
01700 
01701 template <typename T>
01702 void PointMatcherIO<T>::PLYElement::addProperty(
01703                 PLYProperty& prop) 
01704 {
01705         if (prop.pmType == FEATURE)
01706         {
01707                 nbFeatures++;
01708         }
01709         else if (prop.pmType == DESCRIPTOR)
01710         {
01711                 nbDescriptors++;
01712         }
01713                 
01714         properties.push_back(prop);
01715 }
01716 
01717 
01718 template <typename T>
01719 bool PointMatcherIO<T>::PLYElement::supportsProperty(const PLYProperty& prop) const
01720 {
01721         return getPMType(prop.name) != UNSUPPORTED;
01722 }
01723 
01724 
01725 
01726 template <typename T>
01727 typename PointMatcherIO<T>::PLYElementF::ElementTypes PointMatcherIO<T>::PLYElementF::getElementType(const std::string& elem_name)
01728 {
01729         string lc = elem_name;
01730         boost::algorithm::to_lower(lc);
01731         if (lc == "vertex")
01732         {
01733                 return VERTEX;
01734         }
01735         else
01736         {
01737                 return UNSUPPORTED;
01738         }
01739 }
01740 
01741 template <typename T>
01742 bool PointMatcherIO<T>::PLYElementF::elementSupported(const std::string& elem_name)
01743 {
01744         return getElementType(elem_name) != UNSUPPORTED;
01745 }
01746 
01747 template<typename T>
01748 typename PointMatcherIO<T>::PLYElement* PointMatcherIO<T>::PLYElementF::createElement(
01749                 const std::string& elem_name, const int elem_num, const unsigned offset) {
01750         ElementTypes type = getElementType(elem_name);
01751         if (type == VERTEX)
01752                 return new PLYVertex(elem_num, offset);
01753         else
01754                 return NULL;
01755 }
01756 
01757 template<typename T>
01758 bool PointMatcherIO<T>::plyPropTypeValid(const std::string& type) {
01759         return (type == "char" || type == "uchar" || type == "short"
01760                         || type == "ushort" || type == "int" || type == "uint"
01761                         || type == "float" || type == "double");
01762 }
01763 
01764 template <typename T>
01765 bool PointMatcherIO<T>::PLYElement::operator==(const PLYElement& rhs) const
01766 {
01767         return name == rhs.name;
01768 }
01769 
01770 
01771 template <typename T>
01772 bool PointMatcherIO<T>::PLYProperty::operator==(const PLYProperty& rhs) const
01773 {
01774         return name == rhs.name && type == rhs.type;
01775 }
01776 
01779 template<typename T>
01780 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(const string& fileName) {
01781         ifstream ifs(fileName.c_str());
01782         if (!ifs.good())
01783                 throw runtime_error(string("Cannot open file ") + fileName);
01784         return loadPCD(ifs);
01785 }
01786 
01787 template
01788 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(const string& fileName);
01789 template
01790 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(const string& fileName);
01791 
01792 //template
01793 //PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadPCD(istream& is);
01794 //template
01795 //PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadPCD(istream& is);
01796 
01799 template<typename T>
01800 typename PointMatcherIO<T>::DataPoints PointMatcherIO<T>::loadPCD(std::istream& is) {
01801 
01802         //typedef typename DataPoints::Label Label;
01803         //typedef typename DataPoints::Labels Labels;
01804 
01805         size_t numFields = 0;
01806         size_t numDataFields = 0; // takes into account the cound of each field for multi row descriptors
01807         int xFieldCol = -1;
01808         int yFieldCol = -1;
01809         int zFieldCol = -1;
01810 
01811         vector<int> descFieldsToKeep;
01812         map<int,LabelAssociationPair> colToDescPair;
01813         map<string,int> descLabelToNumRows;
01814         map<string,int> descLabelToStartingRows;
01815         vector<int> descDimensions;
01816 
01817         string xFieldType;
01818         string yFieldType;
01819         string zFieldType;
01820 
01821         size_t width = 0;
01822         size_t height = 0;
01823         size_t numPoints;
01824         size_t numPointsR; // redundant value specified in POINTS field
01825 
01826         size_t lineNum = 0;
01827 
01828         while (!is.eof())
01829         {
01830                 string line;
01831                 getline(is, line);
01832 
01833                 // get rid of white spaces before/after
01834                 boost::trim (line);
01835 
01836                 // ignore comments
01837                 if (line.substr(0,1) == "#")
01838                 {
01839                         lineNum++;
01840                         continue;
01841                 }
01842 
01843                 vector<string> tokens;
01844                 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
01845 
01846                 string pcd_version_str;
01847                 if (tokens[0] == "VERSION")
01848                 {
01849                         if (tokens[1] != "0.7" && tokens[1] != ".7")
01850                                 throw runtime_error("PCD Parse Error: Only PCD Version 0.7 is supported");
01851                 }
01852 
01853                 else if (tokens[0] == "FIELDS")
01854                 {
01855                         numFields = tokens.size() - 1;
01856                         numDataFields = numFields; // in case COUNT is not defined in which case we assume 1 data field per field
01857                         for (size_t i = 1; i < tokens.size(); i++)
01858                         {
01859                                 if (tokens[i] == "x")
01860                                         xFieldCol = i - 1;
01861                                 else if (tokens[i] == "y")
01862                                         yFieldCol = i - 1;
01863                                 else if (tokens[i] == "z")
01864                                         zFieldCol = i - 1;
01865 
01866                                 else if(descSublabelRegistered(tokens[i]))
01867                                 {
01868                                         descFieldsToKeep.push_back(i);
01869                                         LabelAssociationPair associationPair = getDescAssociationPair(tokens[i]);
01870 
01871                                         colToDescPair[i] = associationPair;
01872                                         descLabelToNumRows[associationPair.second]++;
01873                                 }
01874                         }
01875                 }
01876 
01877                 else if (tokens[0] == "SIZE")
01878                 {
01879                         if (xFieldCol == -1 || yFieldCol == -1)
01880                                 throw runtime_error("PCD Parse Error: x field or y field not defined");
01881                         if (tokens.size()  - 1 !=  numFields)
01882                                 throw runtime_error("PCD Parse Error: size not defined for all fields");
01883 
01884 //                      try {
01885 //                              xFieldBytes = boost::lexical_cast<int>(tokens[xFieldCol + 1]);
01886 //                              yFieldBytes = boost::lexical_cast<int>(tokens[yFieldCol + 1]);
01887 //                              if (zFieldCol > -1)
01888 //                                      zFieldBytes = boost::lexical_cast<int>(tokens[zFieldCol + 1]);
01889 //                      }
01890 //                      catch (boost::bad_lexical_cast& e)
01891 //                      {
01892 //                              throw runtime_error("PCD Parse Error: invalid size field");
01893 //                      }
01894 
01895                 }
01896 
01897                 else if (tokens[0] == "TYPE")
01898                 {
01899                         if (xFieldCol == -1 || yFieldCol == -1)
01900                                 throw runtime_error("PCD Parse Error: x field or y field not defined");
01901                         if (tokens.size()  - 1 !=  numFields)
01902                                 throw runtime_error("PCD Parse Error: type not defined for all fields");
01903                         xFieldType = tokens[xFieldCol + 1];
01904                         yFieldType = tokens[yFieldCol + 1];
01905 
01906                         if (xFieldType != "I" && xFieldType != "U" && xFieldType != "F" &&
01907                                         yFieldType != "I" && yFieldType != "U" && yFieldType != "F")
01908                                 throw runtime_error("PCD Parse Error: invalid type");
01909 
01910                         if (zFieldCol > -1)
01911                         {
01912                                 zFieldType = tokens[zFieldCol + 1];
01913                                 if (zFieldType != "I" && zFieldType != "U" && zFieldType != "F")
01914                                         throw runtime_error("PCD Parse Error: invalid type");
01915                         }
01916                 }
01917 
01918                 // overwrite descriptor dimension count with values from header
01919                 else if (tokens[0] == "COUNT")
01920                 {
01921                         if (tokens.size() - 1 != numFields)
01922                                 throw runtime_error("PCD Parse Error: COUNT number does not match number of fields");
01923 
01924                         // first get total count including fields we aren't using
01925                         numDataFields = 0;
01926 
01927                         // we need to overwrite the col to desc pair since there will be more
01928                         // columns now that we have several data counts per field
01929                         map<int, LabelAssociationPair> colToDescPair_ = colToDescPair;
01930                         colToDescPair.clear();
01931 
01932                         vector<int>::const_iterator nextFieldToKeepIt = descFieldsToKeep.begin();
01933                         for (size_t i = 1; i < tokens.size(); i++)
01934                         {
01935                                 int count = boost::lexical_cast<int>(tokens[i]);
01936 
01937                                 if ((int)i == *nextFieldToKeepIt)
01938                                 {
01939                                         string descLabel = colToDescPair_[i].second;
01940                                         descLabelToNumRows[descLabel] = count;
01941 
01942                                         for (int p = 0; p < count; p++)
01943                                                 colToDescPair[numDataFields + p] = LabelAssociationPair(p, descLabel);
01944 
01945                                         if (nextFieldToKeepIt != descFieldsToKeep.end())
01946                                                 nextFieldToKeepIt++;
01947                                 }
01948 
01949                                 numDataFields += count;
01950 
01951                         }
01952                 }
01953 
01954                 else if (tokens[0] == "WIDTH")
01955                 {
01956                         try
01957                         {
01958                                 width = boost::lexical_cast<int>(tokens[1]);
01959                         } catch (boost::bad_lexical_cast& e)
01960                         {
01961                                 throw runtime_error("PCD Parse Error: invalid width");
01962                         }
01963                 }
01964 
01965                 else if (tokens[0] == "HEIGHT")
01966                 {
01967                         try
01968                         {
01969                                 height = boost::lexical_cast<int>(tokens[1]);
01970                         } catch (boost::bad_lexical_cast& e)
01971                         {
01972                                 throw runtime_error("PCD Parse Error: invalid width");
01973                         }
01974                 }
01975 
01976                 // ignore viewpoint for now
01977                 else if (tokens[0] == "VIEWPOINT")
01978                 {
01979                         continue;
01980                 }
01981 
01982                 else if (tokens[0] == "POINTS")
01983                 {
01984                         try
01985                         {
01986                                 numPointsR = boost::lexical_cast<int>(tokens[1]);
01987                         }
01988                         catch (boost::bad_lexical_cast& e)
01989                         {
01990                                 throw runtime_error("PCD Parse Error: invalid number of points");
01991                         }
01992                 }
01993 
01994                 else if (tokens[0] == "DATA")
01995                 {
01996                         if (tokens[1] != "ascii")
01997                                 throw runtime_error("PCD Parse Error: only ascii data is supported");
01998 
01999                         break;
02000                 }
02001 
02002                 lineNum++;
02003         }
02004 
02005         // get number of points
02006         numPoints = width * height;
02007 
02008         if (numPoints != numPointsR)
02009                 throw runtime_error("PCD Parse Error: POINTS field does not match WIDTH and HEIGHT fields");
02010 
02011         // prepare features matrix
02012         Matrix features;
02013         if (zFieldCol > -1)
02014                 features = Matrix(4,numPoints);
02015         else
02016                 features = Matrix(3,numPoints);
02017 
02018         // Prepare descriptors
02019         // Do cumulative sum over number of descriptor rows per decriptor to get the starting
02020         // index row of reach descriptor
02021         int cumSum = 0;
02022         for(map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02023         {
02024                 descLabelToStartingRows[it->first] = cumSum;
02025                 cumSum += it->second;
02026         }
02027 
02028         // allocate descriptor vectors
02029         size_t numDescCols = cumSum; // number of descriptor vectors
02030         Matrix descriptors(numDescCols,numPoints);
02031 
02032         // Now read in the data
02033         size_t p = 0; // point count
02034         while (!is.eof())
02035         {
02036                 string line;
02037                 getline(is, line);
02038 
02039                 // get rid of white spaces before/after
02040                 boost::trim (line);
02041 
02042                 // ignore comments
02043                 if (line.substr(0,1) == "#")
02044                 {
02045                         lineNum++;
02046                         continue;
02047                 }
02048 
02049                 vector<string> tokens;
02050                 boost::split(tokens, line, boost::is_any_of("\t\r "), boost::token_compress_on);
02051 
02052                 if (tokens.size() != numDataFields)
02053                         throw runtime_error(string("PCD Parse Error: number of data columns does not match number of fields at line: ") + boost::lexical_cast<string>(lineNum));
02054 
02055                 features(0,p) = boost::lexical_cast<T>(tokens[xFieldCol]);
02056                 features(1,p) = boost::lexical_cast<T>(tokens[yFieldCol]);
02057 
02058                 if (zFieldCol > -1)
02059                 {
02060                         features(2,p) = boost::lexical_cast<float>(tokens[zFieldCol]);
02061                         features(3,p) = 1;
02062                 } else
02063                         features(2,p) = 1;
02064 
02065                 for (map<int,LabelAssociationPair>::const_iterator cit = colToDescPair.begin();
02066                                 cit != colToDescPair.end(); cit++)
02067                 {
02068                         int startingRow = descLabelToStartingRows[cit->second.second];
02069                         descriptors(startingRow + cit->second.first,p) = boost::lexical_cast<T>(tokens[cit->first]);
02070                 }
02071 
02072                 p++;
02073                 lineNum++;
02074 
02075                 if (p == numPoints)
02076                         break;
02077 
02078         }
02079 
02080         if (p != numPoints)
02081         {
02082                 boost::format errorFmt("PCD Parse Error: the number of points in the data %1 is less than the specified number of points %2");
02083                 errorFmt % p % numPoints;
02084                 throw runtime_error(errorFmt.str());
02085         }
02086 
02087         Labels featureLabels;
02088         featureLabels.push_back(Label("x"));
02089         featureLabels.push_back(Label("y"));
02090 
02091         Labels descriptorLabels;
02092         int n = 0;
02093         for (map<string,int>::const_iterator it = descLabelToNumRows.begin(); it != descLabelToNumRows.end(); it++)
02094         {
02095                 descriptorLabels.push_back(Label(it->first,it->second));
02096                 n++;
02097         }
02098 
02099         if (zFieldCol > -1)
02100                 featureLabels.push_back(Label("z"));
02101 
02102         DataPoints out;
02103 
02104         if (numDescCols > 0)
02105                 out = DataPoints(features, featureLabels, descriptors, descriptorLabels);
02106         else
02107                 out = DataPoints(features, featureLabels);
02108         return out;
02109 }
02110 
02111 template<typename T>
02112 void PointMatcherIO<T>::savePCD(const DataPoints& data,
02113                 const std::string& fileName) {
02114         ofstream ofs(fileName.c_str());
02115         if (!ofs.good())
02116                 throw runtime_error(string("Cannot open file ") + fileName);
02117 
02118         const int pointCount(data.features.cols());
02119         const int featCount(data.features.rows());
02120         const int descRows(data.descriptors.rows());
02121         const int descCount(data.descriptorLabels.size());
02122 
02123         if (pointCount == 0)
02124         {
02125                 cerr << "Warning, no points, doing nothing" << endl;
02126                 return;
02127         }
02128 
02129         ofs << "# .PCD v.7 - Point Cloud Data file format\n" <<"VERSION .7\n";
02130         ofs << "FIELDS";
02131 
02132         for (int f=0; f < (featCount - 1); f++)
02133         {
02134                 ofs << " " << data.featureLabels[f].text;
02135         }
02136 
02137         if (descRows == 0)
02138                 ofs << "\n";
02139         else
02140         {
02141                 for (int i = 0; i < descCount; i++)
02142                 {
02143                         ofs << " " << data.descriptorLabels[i].text;
02144                 }
02145                 ofs << "\n";
02146         }
02147 
02148         ofs << "SIZE";
02149         for (int i =0; i < featCount - 1 + descCount; i++)
02150         {
02151                 ofs << " 4"; // for float
02152         }
02153         ofs << "\n";
02154 
02155         ofs << "TYPE";
02156         for (int i =0; i < featCount - 1 + descCount; i++)
02157         {
02158                 ofs << " F"; // for float
02159         }
02160         ofs << "\n";
02161 
02162         ofs << "COUNT";
02163         for (int f = 0; f < featCount - 1 ; f++ )
02164                 ofs << " 1";
02165         if (descCount == 0)
02166                 ofs << "\n";
02167         else
02168         {
02169                 for (int i = 0; i < descCount; i++)
02170                 {
02171                         ofs << " " << data.descriptorLabels[i].span;
02172                 }
02173                 ofs << "\n";
02174         }
02175 
02176         ofs << "WIDTH " << pointCount << "\n";
02177         ofs << "HEIGHT 1\n";
02178         ofs << "POINTS " << pointCount << "\n";
02179         ofs << "DATA ascii\n";
02180 
02181         // write points
02182         for (int p = 0; p < pointCount; ++p)
02183         {
02184                 for (int f = 0; f < featCount - 1; ++f)
02185                 {
02186                         ofs << data.features(f, p);
02187                         if(!(f == featCount-2 && descRows == 0))
02188                                 ofs << " ";
02189                 }
02190                 for (int d = 0; d < descRows; ++d)
02191                 {
02192                         ofs << data.descriptors(d, p);
02193                         if(d != descRows-1)
02194                                 ofs << " ";
02195                 }
02196                 ofs << "\n";
02197         }
02198 
02199         ofs.close();
02200 }
02201 
02202 template
02203 void PointMatcherIO<float>::savePCD(const DataPoints& data, const std::string& fileName);
02204 template
02205 void PointMatcherIO<double>::savePCD(const DataPoints& data, const std::string& fileName);
02206 
02207 template<typename T>
02208 istream & PointMatcherIO<T>::safeGetLine( istream& is, string & t)
02209 {
02210    t.clear();
02211 
02212        // The characters in the stream are read one-by-one using a std::streambuf.
02213        // That is faster than reading them one-by-one using the std::istream.
02214        // Code that uses streambuf this way must be guarded by a sentry object.
02215        // The sentry object performs various tasks,
02216        // such as thread synchronization and updating the stream state.
02217 
02218        std::istream::sentry se(is, true);
02219        std::streambuf* sb = is.rdbuf();
02220 
02221        for(;;) {
02222            int c = sb->sbumpc();
02223            switch (c) {
02224            case '\n':
02225                return is;
02226            case '\r':
02227                if(sb->sgetc() == '\n')
02228                    sb->sbumpc();
02229                return is;
02230            case EOF:
02231                // Also handle the case when the last line has no line ending
02232                if(t.empty())
02233                    is.setstate(std::ios::eofbit);
02234                return is;
02235            default:
02236                t += (char)c;
02237            }
02238        }
02239 }
02240 
02241 


upstream_src
Author(s):
autogenerated on Mon Oct 6 2014 10:27:42