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


libpointmatcher
Author(s):
autogenerated on Mon Sep 14 2015 02:59:06