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


upstream_src
Author(s):
autogenerated on Wed Sep 24 2014 10:42:00