00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include "IO.h"
00037 #include "InspectorsImpl.h"
00038
00039 #include <iostream>
00040 #include <fstream>
00041 #include <stdexcept>
00042 #include <ctype.h>
00043 #include "boost/algorithm/string.hpp"
00044 #include "boost/filesystem.hpp"
00045 #include "boost/filesystem/path.hpp"
00046 #include "boost/filesystem/operations.hpp"
00047 #include "boost/lexical_cast.hpp"
00048
00049
00050 using namespace std;
00051 using namespace PointMatcherSupport;
00052
00053
00054
00055 static std::vector<string> csvLineToVector(const char* line)
00056 {
00057 std::vector<string> parsedLine;
00058 char delimiters[] = " \t,;";
00059 char *token;
00060 char tmpLine[1024];
00061 char *brkt;
00062 strcpy(tmpLine, line);
00063 token = strtok_r(tmpLine, delimiters, &brkt);
00064 if(line[0] != '%')
00065 {
00066 while (token)
00067 {
00068 parsedLine.push_back(string(token));
00069 token = strtok_r(NULL, delimiters, &brkt);
00070 }
00071 }
00072
00073 return parsedLine;
00074 }
00075
00076
00077 CsvElements parseCsvWithHeader(const std::string& fileName)
00078 {
00079 validateFile(fileName);
00080
00081 ifstream is(fileName.c_str());
00082
00083 unsigned elementCount=0;
00084 std::map<string, unsigned> keywordCols;
00085 CsvElements data;
00086
00087 bool firstLine(true);
00088 unsigned lineCount=0;
00089 while (!is.eof())
00090 {
00091 char line[1024];
00092 is.getline(line, sizeof(line));
00093 line[sizeof(line)-1] = 0;
00094
00095 if(firstLine)
00096 {
00097 std::vector<string> header = csvLineToVector(line);
00098
00099 elementCount = header.size();
00100 for(unsigned int i = 0; i < elementCount; i++)
00101 {
00102 keywordCols[header[i]] = i;
00103 }
00104
00105 firstLine = false;
00106 }
00107 else
00108 {
00109 std::vector<string> parsedLine = csvLineToVector(line);
00110 if(parsedLine.size() != elementCount && parsedLine.size() !=0)
00111 {
00112 stringstream errorMsg;
00113 errorMsg << "Error at line " << lineCount+1 << ": expecting " << elementCount << " columns but read " << parsedLine.size() << " elements.";
00114 throw runtime_error(errorMsg.str());
00115 }
00116
00117 for(unsigned int i = 0; i < parsedLine.size(); i++)
00118 {
00119 for(auto it=keywordCols.begin(); it!=keywordCols.end(); it++)
00120 {
00121 if(i == (*it).second)
00122 {
00123 data[(*it).first].push_back(parsedLine[i]);
00124 }
00125 }
00126 }
00127 }
00128
00129 lineCount++;
00130 }
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145 return data;
00146 }
00147
00148
00150 template<typename T>
00151 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):
00152 readingFileName(readingFileName),
00153 referenceFileName(referenceFileName),
00154 configFileName(configFileName),
00155 initialTransformation(initialTransformation),
00156 groundTruthTransformation(groundTruthTransformation),
00157 gravity(gravity)
00158 {}
00159
00160 template struct PointMatcherIO<float>::FileInfo;
00161 template struct PointMatcherIO<double>::FileInfo;
00162
00163
00164 template<typename T>
00165 PointMatcherIO<T>::FileInfoVector::FileInfoVector()
00166 {
00167 }
00168
00170
00183 template<typename T>
00184 PointMatcherIO<T>::FileInfoVector::FileInfoVector(const std::string& fileName, std::string dataPath, std::string configPath)
00185 {
00186 if (dataPath.empty())
00187 {
00188 #if BOOST_FILESYSTEM_VERSION >= 3
00189 dataPath = boost::filesystem::path(fileName).parent_path().string();
00190 #else
00191 dataPath = boost::filesystem::path(fileName).parent_path().file_string();
00192 #endif
00193 }
00194 if (configPath.empty())
00195 {
00196 #if BOOST_FILESYSTEM_VERSION >= 3
00197 configPath = boost::filesystem::path(fileName).parent_path().string();
00198 #else
00199 configPath = boost::filesystem::path(fileName).parent_path().file_string();
00200 #endif
00201 }
00202
00203 const CsvElements data = parseCsvWithHeader(fileName);
00204
00205
00206 const bool found3dInitialTrans(findTransform(data, "iT", 3));
00207 bool found2dInitialTrans(findTransform(data, "iT", 2));
00208 const bool found3dGroundTruthTrans(findTransform(data, "gT", 3));
00209 bool found2dGroundTruthTrans(findTransform(data, "gT", 2));
00210 if (found3dInitialTrans)
00211 found2dInitialTrans = false;
00212 if (found3dGroundTruthTrans)
00213 found2dGroundTruthTrans = false;
00214
00215
00216 if (found3dInitialTrans && found2dGroundTruthTrans)
00217 throw runtime_error("Initial transformation is in 3D but ground-truth is in 2D");
00218 if (found2dInitialTrans && found3dGroundTruthTrans)
00219 throw runtime_error("Initial transformation is in 2D but ground-truth is in 3D");
00220 CsvElements::const_iterator readingIt(data.find("reading"));
00221 if (readingIt == data.end())
00222 throw runtime_error("Error transfering CSV to structure: The header should at least contain \"reading\".");
00223 CsvElements::const_iterator referenceIt(data.find("reference"));
00224 CsvElements::const_iterator configIt(data.find("config"));
00225
00226
00227 const std::vector<string>& readingFileNames = readingIt->second;
00228 const unsigned lineCount = readingFileNames.size();
00229 boost::optional<std::vector<string> > referenceFileNames;
00230 boost::optional<std::vector<string> > configFileNames;
00231 if (referenceIt != data.end())
00232 {
00233 referenceFileNames = referenceIt->second;
00234 assert (referenceFileNames->size() == lineCount);
00235 }
00236 if (configIt != data.end())
00237 {
00238 configFileNames = configIt->second;
00239 assert (configFileNames->size() == lineCount);
00240 }
00241
00242
00243 for(unsigned line=0; line<lineCount; line++)
00244 {
00245 FileInfo info;
00246
00247
00248 info.readingFileName = localToGlobalFileName(dataPath, readingFileNames[line]);
00249 if (referenceFileNames)
00250 info.referenceFileName = localToGlobalFileName(dataPath, (*referenceFileNames)[line]);
00251 if (configFileNames)
00252 info.configFileName = localToGlobalFileName(configPath, (*configFileNames)[line]);
00253
00254
00255 if(found3dInitialTrans)
00256 info.initialTransformation = getTransform(data, "iT", 3, line);
00257 if(found2dInitialTrans)
00258 info.initialTransformation = getTransform(data, "iT", 2, line);
00259 if(found3dGroundTruthTrans)
00260 info.groundTruthTransformation = getTransform(data, "gT", 3, line);
00261 if(found2dGroundTruthTrans)
00262 info.groundTruthTransformation = getTransform(data, "gT", 2, line);
00263
00264
00265 this->push_back(info);
00266 }
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 }
00281
00283 template<typename T>
00284 std::string PointMatcherIO<T>::FileInfoVector::localToGlobalFileName(const std::string& parentPath, const std::string& fileName)
00285 {
00286 std::string globalFileName(fileName);
00287 if (!boost::filesystem::exists(globalFileName))
00288 {
00289 const boost::filesystem::path globalFilePath(boost::filesystem::path(parentPath) / boost::filesystem::path(fileName));
00290 #if BOOST_FILESYSTEM_VERSION >= 3
00291 globalFileName = globalFilePath.string();
00292 #else
00293 globalFileName = globalFilePath.file_string();
00294 #endif
00295 }
00296 validateFile(globalFileName);
00297 return globalFileName;
00298 }
00299
00301 template<typename T>
00302 bool PointMatcherIO<T>::FileInfoVector::findTransform(const CsvElements& data, const std::string& prefix, unsigned dim)
00303 {
00304 bool found(true);
00305 for(unsigned i=0; i<dim+1; i++)
00306 {
00307 for(unsigned j=0; j<dim+1; j++)
00308 {
00309 stringstream transName;
00310 transName << prefix << i << j;
00311 found = found && (data.find(transName.str()) != data.end());
00312 }
00313 }
00314 return found;
00315 }
00316
00318 template<typename T>
00319 typename PointMatcherIO<T>::TransformationParameters PointMatcherIO<T>::FileInfoVector::getTransform(const CsvElements& data, const std::string& prefix, unsigned dim, unsigned line)
00320 {
00321 TransformationParameters transformation(TransformationParameters::Identity(dim+1, dim+1));
00322 for(unsigned i=0; i<dim+1; i++)
00323 {
00324 for(unsigned j=0; j<dim+1; j++)
00325 {
00326 stringstream transName;
00327 transName << prefix << i << j;
00328 CsvElements::const_iterator colIt(data.find(transName.str()));
00329 const T value = boost::lexical_cast<T> (colIt->second[line]);
00330 transformation(i,j) = value;
00331 }
00332 }
00333 return transformation;
00334 }
00335
00336 template struct PointMatcherIO<float>::FileInfoVector;
00337 template struct PointMatcherIO<double>::FileInfoVector;
00338
00340 void PointMatcherSupport::validateFile(const std::string& fileName)
00341 {
00342 boost::filesystem::path fullPath(fileName);
00343
00344 ifstream ifs(fileName.c_str());
00345 if (!ifs.good())
00346 #if BOOST_FILESYSTEM_VERSION >= 3
00347 #if BOOST_VERSION >= 105000
00348 throw runtime_error(string("Cannot open file ") + boost::filesystem::complete(fullPath).native());
00349 #else
00350 throw runtime_error(string("Cannot open file ") + boost::filesystem3::complete(fullPath).native());
00351 #endif
00352 #else
00353 throw runtime_error(string("Cannot open file ") + boost::filesystem::complete(fullPath).native_file_string());
00354 #endif
00355 }
00356
00357
00359 template<typename T>
00360 typename PointMatcher<T>::DataPoints PointMatcher<T>::DataPoints::load(const std::string& fileName)
00361 {
00362 const boost::filesystem::path path(fileName);
00363 const string& ext(boost::filesystem::extension(path));
00364 if (boost::iequals(ext, ".vtk"))
00365 return PointMatcherIO<T>::loadVTK(fileName);
00366 else if (boost::iequals(ext, ".csv"))
00367 return PointMatcherIO<T>::loadCSV(fileName);
00368 else
00369 throw runtime_error("loadAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
00370 }
00371
00372 template
00373 PointMatcher<float>::DataPoints PointMatcher<float>::DataPoints::load(const std::string& fileName);
00374 template
00375 PointMatcher<double>::DataPoints PointMatcher<double>::DataPoints::load(const std::string& fileName);
00376
00377
00390 template<typename T>
00391 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(const std::string& fileName)
00392 {
00393 ifstream ifs(fileName.c_str());
00394
00395 validateFile(fileName);
00396
00397 return loadCSV(ifs);
00398 }
00399
00402 template<typename T>
00403 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadCSV(std::istream& is)
00404 {
00405 typedef typename DataPoints::Label Label;
00406 typedef typename DataPoints::Labels Labels;
00407
00408 vector<T> xData;
00409 vector<T> yData;
00410 vector<T> zData;
00411 vector<T> padData;
00412 vector<string> header;
00413 int dim(0);
00414 bool firstLine(true);
00415 bool hasHeader(false);
00416 Labels labels;
00417 int xCol(-1);
00418 int yCol(-1);
00419 int zCol(-1);
00420
00421 char delimiters[] = " \t,;";
00422 char *token;
00423 while (!is.eof())
00424 {
00425 char line[1024];
00426 is.getline(line, sizeof(line));
00427 line[sizeof(line)-1] = 0;
00428
00429
00430 unsigned int len = strspn(line, " ,+-.1234567890Ee");
00431 if(len != strlen(line))
00432 {
00433
00434 hasHeader = true;
00435 }
00436 else
00437 {
00438 hasHeader = false;
00439 }
00440
00441
00442 if(firstLine)
00443 {
00444 char tmpLine[1024];
00445 char *brkt;
00446 strcpy(tmpLine, line);
00447 token = strtok_r(tmpLine, delimiters, &brkt);
00448 while (token)
00449 {
00450 dim++;
00451
00452
00453 if(hasHeader)
00454 {
00455 header.push_back(string(token));
00456 }
00457
00458 token = strtok_r(NULL, delimiters, &brkt);
00459 }
00460
00461
00462 if (hasHeader)
00463 {
00464
00465 for(unsigned int i = 0; i < header.size(); i++)
00466 {
00467 if(header[i].compare("x") == 0)
00468 xCol = i;
00469
00470 if(header[i].compare("y") == 0)
00471 yCol = i;
00472
00473 if(header[i].compare("z") == 0)
00474 zCol = i;
00475 }
00476 if(xCol == -1 || yCol == -1)
00477 {
00478 for(unsigned int i = 0; i < header.size(); i++)
00479 {
00480 cout << "(" << i << ") " << header[i] << endl;
00481 }
00482 cout << endl << "Enter ID for x: ";
00483 cin >> xCol;
00484 cout << "Enter ID for y: ";
00485 cin >> yCol;
00486 cout << "Enter ID for z (-1 if 2D data): ";
00487 cin >> zCol;
00488 }
00489 }
00490 else
00491 {
00492
00493 if (!(dim == 2 || dim == 3))
00494 {
00495 cout << "WARNING: " << dim << " columns detected. Not obivious which columns to load for x, y or z." << endl;
00496 cout << endl << "Enter column ID (starting from 0) for x: ";
00497 cin >> xCol;
00498 cout << "Enter column ID (starting from 0) for y: ";
00499 cin >> yCol;
00500 cout << "Enter column ID (starting from 0, -1 if 2D data) for z: ";
00501 cin >> zCol;
00502 }
00503 else
00504 {
00505
00506 xCol = 0;
00507 yCol = 1;
00508 if(dim == 3)
00509 zCol = 2;
00510 }
00511 }
00512
00513 if(zCol != -1)
00514 dim = 3;
00515 else
00516 dim = 2;
00517 }
00518
00519
00520 char *brkt;
00521 token = strtok_r(line, delimiters, &brkt);
00522 int currentCol = 0;
00523 while (token)
00524 {
00525
00526 if(!hasHeader)
00527 {
00528 if(currentCol == xCol)
00529 xData.push_back(atof(token));
00530 if(currentCol == yCol)
00531 yData.push_back(atof(token));
00532 if(currentCol == zCol)
00533 zData.push_back(atof(token));
00534 }
00535
00536 token = strtok_r(NULL, delimiters, &brkt);
00537 currentCol++;
00538 }
00539
00540
00541 padData.push_back(1);
00542
00543 if (firstLine)
00544 {
00545
00546 for (int i=0; i < dim; i++)
00547 {
00548 string text;
00549 text += char('x' + i);
00550 labels.push_back(Label(text, 1));
00551 }
00552 labels.push_back(Label("pad", 1));
00553 }
00554
00555 firstLine = false;
00556 }
00557
00558 assert(xData.size() == yData.size());
00559 int nbPoints = xData.size();
00560
00561
00562 Matrix features(dim+1, nbPoints);
00563 for(int i=0; i < nbPoints; i++)
00564 {
00565 features(0,i) = xData[i];
00566 features(1,i) = yData[i];
00567 if(dim == 3)
00568 {
00569 features(2,i) = zData[i];
00570 features(3,i) = 1;
00571 }
00572 else
00573 {
00574 features(2,i) = 1;
00575 }
00576 }
00577
00578 DataPoints dataPoints(features, labels);
00579
00580
00581
00582
00583 return dataPoints;
00584 }
00585
00586 template
00587 PointMatcher<float>::DataPoints PointMatcherIO<float>::loadCSV(const std::string& fileName);
00588 template
00589 PointMatcher<double>::DataPoints PointMatcherIO<double>::loadCSV(const std::string& fileName);
00590
00592 template<typename T>
00593 void PointMatcher<T>::DataPoints::save(const std::string& fileName) const
00594 {
00595 const boost::filesystem::path path(fileName);
00596 const string& ext(boost::filesystem::extension(path));
00597 if (boost::iequals(ext, ".vtk"))
00598 return PointMatcherIO<T>::saveVTK(*this, fileName);
00599 else if (boost::iequals(ext, ".csv"))
00600 return PointMatcherIO<T>::saveCSV(*this, fileName);
00601 else
00602 throw runtime_error("saveAnyFormat(): Unknown extension \"" + ext + "\" for file \"" + fileName + "\", extension must be either \".vtk\" or \".csv\"");
00603 }
00604
00605 template
00606 void PointMatcher<float>::DataPoints::save(const std::string& fileName) const;
00607 template
00608 void PointMatcher<double>::DataPoints::save(const std::string& fileName) const;
00609
00611 template<typename T>
00612 void PointMatcherIO<T>::saveCSV(const DataPoints& data, const std::string& fileName)
00613 {
00614 ofstream ofs(fileName.c_str());
00615 if (!ofs.good())
00616 throw runtime_error(string("Cannot open file ") + fileName);
00617 saveCSV(data, ofs);
00618 }
00619
00621 template<typename T>
00622 void PointMatcherIO<T>::saveCSV(const DataPoints& data, std::ostream& os)
00623 {
00624 const int pointCount(data.features.cols());
00625 const int dimCount(data.features.rows());
00626
00627 if (pointCount == 0)
00628 {
00629 cerr << "Warning, no points, doing nothing" << endl;
00630 return;
00631 }
00632
00633
00634 for (int p = 0; p < pointCount; ++p)
00635 {
00636 for (int i = 0; i < dimCount-1; ++i)
00637 {
00638 os << data.features(i, p);
00639 if(i != dimCount-2)
00640 os << " , ";
00641 }
00642 os << "\n";
00643 }
00644
00645 }
00646
00647 template
00648 void PointMatcherIO<float>::saveCSV(const DataPoints& data, const std::string& fileName);
00649 template
00650 void PointMatcherIO<double>::saveCSV(const DataPoints& data, const std::string& fileName);
00651
00653 template<typename T>
00654 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(const std::string& fileName)
00655 {
00656 ifstream ifs(fileName.c_str());
00657 if (!ifs.good())
00658 throw runtime_error(string("Cannot open file ") + fileName);
00659 return loadVTK(ifs);
00660 }
00661
00663 template<typename T>
00664 typename PointMatcher<T>::DataPoints PointMatcherIO<T>::loadVTK(std::istream& is)
00665 {
00666 typedef typename DataPoints::Label Label;
00667 typedef typename DataPoints::Labels Labels;
00668
00669 DataPoints loadedPoints;
00670
00671
00672 string line;
00673 getline(is, line);
00674 if (line.find("# vtk DataFile Version") != 0)
00675 throw runtime_error(string("Wrong magic header, found ") + line);
00676 getline(is, line);
00677 getline(is, line);
00678 if (line != "ASCII")
00679 throw runtime_error(string("Wrong file type, expecting ASCII, found ") + line);
00680 getline(is, line);
00681 if (line != "DATASET POLYDATA")
00682 throw runtime_error(string("Wrong data type, expecting DATASET POLYDATA, found ") + line);
00683
00684
00685
00686 string fieldName;
00687 string name;
00688 int pointCount = 0;
00689 string type;
00690 while (is.good())
00691 {
00692 is >> fieldName;
00693
00694
00695 if(fieldName == "POINTS")
00696 {
00697 is >> pointCount;
00698 is >> type;
00699
00700 if(!(type == "float" || type == "double"))
00701 throw runtime_error(string("Field POINTS can only be of type double or float"));
00702
00703 Matrix features(4, pointCount);
00704 for (int p = 0; p < pointCount; ++p)
00705 {
00706 is >> features(0, p);
00707 is >> features(1, p);
00708 is >> features(2, p);
00709 features(3, p) = 1.0;
00710 }
00711 loadedPoints.addFeature("x", features.row(0));
00712 loadedPoints.addFeature("y", features.row(1));
00713 loadedPoints.addFeature("z", features.row(2));
00714 loadedPoints.addFeature("pad", features.row(3));
00715 }
00716 else if(fieldName == "VERTICES")
00717 {
00718 int size;
00719 int verticeSize;
00720 is >> size >> verticeSize;
00721
00722 for (int p = 0; p < pointCount; p++)
00723 {
00724 getline(is, line);
00725 if(line == "")
00726 p--;
00727 }
00728 }
00729 else if(fieldName == "POINT_DATA")
00730 {
00731 int descriptorCount;
00732 is >> descriptorCount;
00733 if(pointCount != descriptorCount)
00734 throw runtime_error(string("The size of POINTS is different than POINT_DATA"));
00735 }
00736 else
00737 {
00738
00739 is >> name;
00740
00741 int dim = 0;
00742 bool skipLookupTable = false;
00743 if(fieldName == "SCALARS")
00744 {
00745 dim = 1;
00746 is >> type;
00747 skipLookupTable = true;
00748 }
00749 else if(fieldName == "VECTORS")
00750 {
00751 dim = 3;
00752 is >> type;
00753 }
00754 else if(fieldName == "TENSORS")
00755 {
00756 dim = 9;
00757 is >> type;
00758 }
00759 else if(fieldName == "NORMALS")
00760 {
00761 dim = 3;
00762 is >> type;
00763 }
00764 else if(fieldName == "COLOR_SCALARS")
00765 {
00766 is >> dim;
00767 type = "float";
00768 }
00769 else
00770 throw runtime_error(string("Unknown field name " + fieldName + ", expecting SCALARS, VECTORS, TENSORS, NORMALS or COLOR_SCALARS."));
00771
00772
00773 if(!(type == "float" || type == "double"))
00774 throw runtime_error(string("Field " + fieldName + " is " + type + " but can only be of type double or float"));
00775
00776
00777 if(skipLookupTable)
00778 {
00779
00780 getline(is, line);
00781 getline(is, line);
00782 }
00783
00784 Matrix descriptor(dim, pointCount);
00785 for (int p = 0; p < pointCount; ++p)
00786 {
00787 for(int d = 0; d < dim; d++)
00788 {
00789 is >> descriptor(d, p);
00790 }
00791 }
00792 loadedPoints.addDescriptor(name, descriptor);
00793 }
00794
00795 }
00796
00797 return loadedPoints;
00798 }
00799
00800 template
00801 PointMatcherIO<float>::DataPoints PointMatcherIO<float>::loadVTK(const std::string& fileName);
00802 template
00803 PointMatcherIO<double>::DataPoints PointMatcherIO<double>::loadVTK(const std::string& fileName);
00804
00805
00807 template<typename T>
00808 void PointMatcherIO<T>::saveVTK(const DataPoints& data, const std::string& fileName)
00809 {
00810 Parametrizable::Parameters param({{"baseFileName", ""}});
00811 typedef typename InspectorsImpl<T>::VTKFileInspector VTKInspector;
00812 VTKInspector vtkInspector(param);
00813 vtkInspector.dumpDataPoints(data, fileName);
00814 }
00815
00816
00817 template
00818 void PointMatcherIO<float>::saveVTK(const PointMatcherIO<float>::DataPoints& data, const std::string& fileName);
00819 template
00820 void PointMatcherIO<double>::saveVTK(const PointMatcher<double>::DataPoints& data, const std::string& fileName);