47 #include "boost/algorithm/string.hpp"
48 #include "boost/filesystem.hpp"
49 #include "boost/filesystem/path.hpp"
50 #include "boost/filesystem/operations.hpp"
51 #include "boost/lexical_cast.hpp"
52 #include "boost/foreach.hpp"
55 #define strtok_r strtok_s
62 const bool isBigEndian = *
reinterpret_cast<const unsigned char*
>(&one) ==
static_cast<unsigned char>(0);
73 std::vector<string> parsedLine;
74 char delimiters[] =
" \t,;";
78 strcpy(tmpLine, line);
79 token = strtok_r(tmpLine, delimiters, &brkt);
84 parsedLine.push_back(
string(token));
85 token = strtok_r(NULL, delimiters, &brkt);
97 ifstream is(fileName.c_str());
99 unsigned elementCount=0;
100 std::map<string, unsigned> keywordCols;
103 bool firstLine(
true);
104 unsigned lineCount=0;
114 elementCount = header.size();
115 for(
unsigned int i = 0; i < elementCount; i++)
117 keywordCols[header[i]] = i;
125 if(parsedLine.size() != elementCount && parsedLine.size() !=0)
127 stringstream errorMsg;
128 errorMsg <<
"Error at line " << lineCount+1 <<
": expecting " << elementCount <<
" columns but read " << parsedLine.size() <<
" elements.";
129 throw runtime_error(errorMsg.str());
132 for(
unsigned int i = 0; i < parsedLine.size(); i++)
134 for(BOOST_AUTO(it,keywordCols.begin()); it!=keywordCols.end(); it++)
136 if(i == (*it).second)
138 data[(*it).first].push_back(parsedLine[i]);
167 readingFileName(readingFileName),
168 referenceFileName(referenceFileName),
169 configFileName(configFileName),
170 initialTransformation(initialTransformation),
171 groundTruthTransformation(groundTruthTransformation),
203 #if BOOST_FILESYSTEM_VERSION >= 3
204 dataPath = boost::filesystem::path(fileName).parent_path().string();
206 dataPath = boost::filesystem::path(fileName).parent_path().file_string();
209 if (configPath.empty())
211 #if BOOST_FILESYSTEM_VERSION >= 3
212 configPath = boost::filesystem::path(fileName).parent_path().string();
214 configPath = boost::filesystem::path(fileName).parent_path().file_string();
221 const bool found3dInitialTrans(findTransform(
data,
"iT", 3));
222 bool found2dInitialTrans(findTransform(
data,
"iT", 2));
223 const bool found3dGroundTruthTrans(findTransform(
data,
"gT", 3));
224 bool found2dGroundTruthTrans(findTransform(
data,
"gT", 2));
225 if (found3dInitialTrans)
226 found2dInitialTrans =
false;
227 if (found3dGroundTruthTrans)
228 found2dGroundTruthTrans =
false;
231 if (found3dInitialTrans && found2dGroundTruthTrans)
232 throw runtime_error(
"Initial transformation is in 3D but ground-truth is in 2D");
233 if (found2dInitialTrans && found3dGroundTruthTrans)
234 throw runtime_error(
"Initial transformation is in 2D but ground-truth is in 3D");
235 CsvElements::const_iterator readingIt(
data.find(
"reading"));
236 if (readingIt ==
data.end())
237 throw runtime_error(
"Error transfering CSV to structure: The header should at least contain \"reading\".");
238 CsvElements::const_iterator referenceIt(
data.find(
"reference"));
239 CsvElements::const_iterator configIt(
data.find(
"config"));
242 const std::vector<string>& readingFileNames = readingIt->second;
243 const unsigned lineCount = readingFileNames.size();
244 boost::optional<std::vector<string> > referenceFileNames;
245 boost::optional<std::vector<string> > configFileNames;
246 if (referenceIt !=
data.end())
248 referenceFileNames = referenceIt->second;
249 assert (referenceFileNames->size() == lineCount);
251 if (configIt !=
data.end())
253 configFileNames = configIt->second;
254 assert (configFileNames->size() == lineCount);
258 for(
unsigned line=0; line<lineCount; line++)
264 if (referenceFileNames)
267 info.
configFileName = localToGlobalFileName(configPath, (*configFileNames)[line]);
270 if(found3dInitialTrans)
272 if(found2dInitialTrans)
274 if(found3dGroundTruthTrans)
276 if(found2dGroundTruthTrans)
280 this->push_back(info);
302 if (!boost::filesystem::exists(globalFileName))
304 const boost::filesystem::path globalFilePath(boost::filesystem::path(parentPath) / boost::filesystem::path(fileName));
305 #if BOOST_FILESYSTEM_VERSION >= 3
306 globalFileName = globalFilePath.string();
308 globalFileName = globalFilePath.file_string();
312 return globalFileName;
320 for(
unsigned i=0; i<
dim+1; i++)
322 for(
unsigned j=0; j<
dim+1; j++)
324 stringstream transName;
325 transName << prefix << i << j;
326 found = found && (
data.find(transName.str()) !=
data.end());
337 for(
unsigned i=0; i<
dim+1; i++)
339 for(
unsigned j=0; j<
dim+1; j++)
341 stringstream transName;
342 transName << prefix << i << j;
343 CsvElements::const_iterator colIt(
data.find(transName.str()));
344 const T value = boost::lexical_cast<T> (colIt->second[line]);
357 boost::filesystem::path fullPath(fileName);
359 ifstream ifs(fileName.c_str());
360 if (!ifs.good() || !boost::filesystem::is_regular_file(fullPath))
361 #if BOOST_FILESYSTEM_VERSION >= 3
362 #if BOOST_VERSION >= 105000
363 throw runtime_error(
string(
"Cannot open file ") + boost::filesystem::complete(fullPath).generic_string());
365 throw runtime_error(
string(
"Cannot open file ") + boost::filesystem3::complete(fullPath).generic_string());
368 throw runtime_error(
string(
"Cannot open file ") + boost::filesystem::complete(fullPath).native_file_string());
377 const boost::filesystem::path path(fileName);
378 const string& ext(boost::filesystem::extension(path));
379 if (boost::iequals(ext,
".vtk"))
381 else if (boost::iequals(ext,
".csv"))
383 else if (boost::iequals(ext,
".ply"))
385 else if (boost::iequals(ext,
".pcd"))
388 throw runtime_error(
"loadAnyFormat(): Unknown extension \"" + ext +
"\" for file \"" + fileName +
"\", extension must be either \".vtk\" or \".csv\"");
410 ifstream ifs(fileName.c_str());
419 internalName(internalName),
420 externalName(externalName),
430 bool findLabel =
false;
431 for(
size_t i=0; i<labels.size(); ++i)
433 if(internalName == labels[i].text)
444 labels.push_back(
Label(internalName,1));
451 labels.push_back(
Label(internalName,
dim));
466 template <
typename T>
472 if (label.
text ==
"normals")
487 else if (label.
text ==
"color")
491 externalName =
"red";
495 externalName =
"green";
499 externalName =
"blue";
502 externalName =
"alpha";
504 else if (label.
text ==
"eigValues")
506 externalName =
"eigValues" + boost::lexical_cast<string>(row);
508 else if (label.
text ==
"eigVectors")
511 externalName =
"eigVectors" + boost::lexical_cast<string>(row/3);
513 int row_mod = row % 3;
516 else if (row_mod == 1)
518 else if (row_mod == 2)
521 else if (label.
span == 1)
523 externalName = label.
text;
526 externalName = label.
text + boost::lexical_cast<std::string>(row);
537 vector<GenericInputHeader> csvHeader;
542 unsigned int csvCol = 0;
543 unsigned int csvRow = 0;
544 bool hasHeader(
false);
545 bool firstLine(
true);
549 is.unsetf(std::ios_base::skipws);
550 unsigned int line_count = std::count(
551 std::istream_iterator<char>(is),
552 std::istream_iterator<char>(),
556 is.seekg(0, ios::beg);
558 char delimiters[] =
" \t,;";
569 unsigned int len = strspn(line.c_str(),
" ,+-.1234567890Ee");
570 if(len != line.length())
583 unsigned int dim = 0;
585 strcpy(tmpLine, line.c_str());
587 token = strtok_r(tmpLine, delimiters, &brkt);
598 token = strtok_r(NULL, delimiters, &brkt);
604 if (!(
dim == 2 ||
dim == 3))
606 int idX=0, idY=0, idZ=0;
608 cout <<
"WARNING: " <<
dim <<
" columns detected. Not obvious which columns to load for x, y or z." << endl;
609 cout << endl <<
"Enter column ID (starting from 0) for x: ";
611 cout <<
"Enter column ID (starting from 0) for y: ";
613 cout <<
"Enter column ID (starting from 0, -1 if 2D data) for z: ";
617 for(
unsigned int i=0; i<
dim; i++)
619 std::ostringstream os;
646 int rowIdFeatures = 0;
647 int rowIdDescriptors = 0;
652 for(
size_t i=0; i<externalLabels.size(); i++)
656 for(
size_t j=0; j < csvHeader.size(); j++)
660 csvHeader[j].matrixType = supLabel.
type;
662 switch (supLabel.
type)
665 csvHeader[j].matrixRowId = rowIdFeatures;
670 csvHeader[j].matrixRowId = rowIdDescriptors;
675 csvHeader[j].matrixRowId = rowIdTime;
680 throw runtime_error(
string(
"CSV parse error: encounter a type different from FEATURE, DESCRIPTOR and TIME. Implementation not supported. See the definition of 'enum PMPropTypes'"));
691 for(
unsigned int i=0; i<csvHeader.size(); i++)
696 csvHeader[i].matrixRowId = rowIdDescriptors;
697 descLabelGen.
add(csvHeader[i].
name);
704 if(hasHeader && line_count > 0)
710 const unsigned int nbPoints = line_count;
712 features =
Matrix(featDim, nbPoints);
713 descriptors =
Matrix(descDim, nbPoints);
721 strcpy(line_c,line.c_str());
722 token = strtok_r(line_c, delimiters, &brkt);
724 if(!(hasHeader && firstLine))
730 if(csvCol > (csvHeader.size() - 1))
734 (boost::format(
"CSV parse error: at line %1%, too many elements to parse compare to the header number of columns (col=%2%).") % csvRow % csvHeader.size()).str());
738 const int matrixRow = csvHeader[csvCol].matrixRowId;
739 const int matrixCol = csvRow;
741 switch (csvHeader[csvCol].matrixType)
744 features(matrixRow, matrixCol) = lexical_cast_scalar_to_string<T>(
string(token));
747 descriptors(matrixRow, matrixCol) = lexical_cast_scalar_to_string<T>(token);
750 times(matrixRow, matrixCol) = lexical_cast_scalar_to_string<std::int64_t>(token);
753 throw runtime_error(
string(
"CSV parse error: encounter a type different from FEATURE, DESCRIPTOR and TIME. Implementation not supported. See the definition of 'enum PMPropTypes'"));
759 token = strtok_r(NULL, delimiters, &brkt);
765 if(csvCol != (csvHeader.size()))
768 (boost::format(
"CSV parse error: at line %1%, not enough elements to parse compare to the header number of columns (col=%2%).") % csvRow % csvHeader.size()).str());
781 if (descriptors.rows() > 0)
789 loadedPoints.
times = times;
796 loadedPoints.
addFeature(
"pad", Matrix::Ones(1,features.cols()));
811 const boost::filesystem::path path(fileName);
812 const string& ext(boost::filesystem::extension(path));
813 if (boost::iequals(ext,
".vtk"))
817 throw runtime_error(
"save(): Binary writing is not supported together with extension \"" + ext +
"\". Currently binary writing is only supported with \".vtk\".");
819 if (boost::iequals(ext,
".csv"))
821 else if (boost::iequals(ext,
".ply"))
823 else if (boost::iequals(ext,
".pcd"))
826 throw runtime_error(
"save(): Unknown extension \"" + ext +
"\" for file \"" + fileName +
"\", extension must be either \".vtk\", \".ply\", \".pcd\" or \".csv\"");
838 ofstream ofs(fileName.c_str());
840 throw runtime_error(
string(
"Cannot open file ") + fileName);
841 ofs.precision(precision);
849 const int pointCount(
data.features.cols());
850 const int dimCount(
data.features.rows());
851 const int descDimCount(
data.descriptors.rows());
860 for (
int i = 0; i < dimCount - 1; i++)
862 os <<
data.featureLabels[i].text;
864 if (!((i == (dimCount - 2)) && descDimCount == 0))
869 for (
size_t i = 0; i <
data.descriptorLabels.size(); i++)
872 for (
size_t s = 0; s < lab.
span; s++)
875 if (n != (descDimCount - 1))
884 for (
int p = 0; p < pointCount; ++p)
886 for (
int i = 0; i < dimCount-1; ++i)
888 os <<
data.features(i, p);
889 if(!((i == (dimCount - 2)) && descDimCount == 0))
893 for (
int i = 0; i < descDimCount; i++)
895 os <<
data.descriptors(i,p);
896 if (i != (descDimCount - 1))
913 ifstream ifs(fileName.c_str(), std::ios::binary);
915 throw runtime_error(
string(
"Cannot open file ") + fileName);
919 void skipBlock(
bool binary,
int binarySize, std::istream & is,
bool hasSeparateSizeParameter =
true){
924 throw std::runtime_error(
"File violates the VTK format : parameter 'n' is missing after a field name.");
927 if(hasSeparateSizeParameter) {
930 throw std::runtime_error(
"File violates the VTK format : parameter 'size' is missing after a field name.");
939 is.seekg(size * binarySize, std::ios_base::cur);
941 for (
int p = 0; p < n; p++)
952 std::map<std::string, SplitTime> labelledSplitTime;
959 if (line.find(
"# vtk DataFile Version") != 0)
960 throw runtime_error(
string(
"Wrong magic header, found ") + line);
964 const bool isBinary = (line ==
"BINARY");
965 if (line !=
"ASCII"){
967 throw runtime_error(
string(
"Wrong file type, expecting ASCII or BINARY, found ") + line);
973 if (line ==
"DATASET POLYDATA")
975 else if (line ==
"DATASET UNSTRUCTURED_GRID")
978 throw runtime_error(
string(
"Wrong data type, expecting DATASET POLYDATA, found ") + line);
988 while (is >> fieldName)
991 if(fieldName ==
"POINTS")
997 if(!(type ==
"float" || type ==
"double"))
998 throw runtime_error(
string(
"Field POINTS can only be of type double or float"));
1000 Matrix features(4, pointCount);
1001 for (
int p = 0; p < pointCount; ++p)
1003 readVtkData(type, isBinary, features.template block<3, 1>(0, p), is);
1004 features(3, p) = 1.0;
1006 loadedPoints.
addFeature(
"x", features.row(0));
1007 loadedPoints.
addFeature(
"y", features.row(1));
1008 loadedPoints.
addFeature(
"z", features.row(2));
1009 loadedPoints.
addFeature(
"pad", features.row(3));
1015 else if(dataType ==
POLYDATA && fieldName ==
"VERTICES")
1020 else if(dataType ==
POLYDATA && fieldName ==
"LINES")
1025 else if(dataType ==
POLYDATA && fieldName ==
"POLYGONS")
1030 else if(dataType ==
POLYDATA && fieldName ==
"TRIANGLE_STRIPS")
1047 else if(fieldName ==
"POINT_DATA")
1049 int descriptorCount;
1050 is >> descriptorCount;
1051 if(pointCount != descriptorCount)
1052 throw runtime_error(
string(
"The size of POINTS is different than POINT_DATA"));
1056 else if (fieldName ==
"FIELD")
1058 string fieldDataName;
1060 is >> fieldDataName >> fieldDataCount;
1062 for (
int f = 0; f < fieldDataCount; f++)
1065 is >>
name >>
dim >> numTuples >> type;
1067 if(type ==
"vtkIdType")
1070 is.seekg(
dim * numTuples * 4, std::ios_base::cur);
1073 for (
int t = 0; t <
dim * numTuples; t++ )
1079 else if(!(type ==
"float" || type ==
"double"))
1080 throw runtime_error(
string(
"Field " + fieldName +
" is " + type +
" but can only be of type double or float"));
1084 readVtkData(type, isBinary, descriptor.transpose(), is);
1088 else if(fieldName ==
"METADATA")
1092 while(!line.empty())
1103 bool isTimeSec =
false;
1104 bool isTimeNsec =
false;
1107 if(boost::algorithm::ends_with(
name,
"_splitTime_high32"))
1110 boost::algorithm::erase_last(
name,
"_splitTime_high32");
1113 if(boost::algorithm::ends_with(
name,
"_splitTime_low32"))
1116 boost::algorithm::erase_last(
name,
"_splitTime_low32");
1120 bool skipLookupTable =
false;
1121 bool isColorScalars =
false;
1122 if(fieldName ==
"SCALARS")
1126 skipLookupTable =
true;
1128 else if(fieldName ==
"VECTORS")
1133 else if(fieldName ==
"TENSORS")
1138 else if(fieldName ==
"NORMALS")
1143 else if(fieldName ==
"COLOR_SCALARS")
1147 isColorScalars =
true;
1150 throw runtime_error(
string(
"Unknown field name " + fieldName +
", expecting SCALARS, VECTORS, TENSORS, NORMALS or COLOR_SCALARS."));
1157 if(isTimeSec || isTimeNsec)
1165 typename std::map<std::string, SplitTime>::iterator it;
1167 it = labelledSplitTime.find(
name);
1169 if(it == labelledSplitTime.end())
1172 t.
high32 = Eigen::Matrix<unsigned int, Eigen::Dynamic, Eigen::Dynamic> (
dim, pointCount);
1174 labelledSplitTime[
name] = t;
1180 assert(labelledSplitTime[
name].isHigh32Found ==
false);
1181 readVtkData(type, isBinary, labelledSplitTime[
name].high32.transpose(), is);
1182 labelledSplitTime[
name].isHigh32Found =
true;
1188 assert(labelledSplitTime[
name].isLow32Found ==
false);
1189 readVtkData(type, isBinary, labelledSplitTime[
name].low32.transpose(), is);
1190 labelledSplitTime[
name].isLow32Found =
true;
1198 if(isColorScalars && isBinary)
1200 std::vector<unsigned char> buffer(
dim);
1201 for (
int i = 0; i < pointCount; ++i){
1202 is.read(
reinterpret_cast<char *
>(&buffer.front()),
dim);
1203 for(
int r=0; r <
dim; ++r){
1204 descriptorData(r, i) = buffer[r] /
static_cast<T>(255.0);
1210 if(!(type ==
"float" || type ==
"double"))
1211 throw runtime_error(
string(
"Field " + fieldName +
" is " + type +
" but can only be of type double or float."));
1218 readVtkData(type, isBinary, descriptorData.transpose(), is);
1226 typename std::map<std::string, SplitTime>::iterator it;
1227 for(it=labelledSplitTime.begin(); it!=labelledSplitTime.end(); it++)
1230 if(it->second.isHigh32Found ==
false)
1232 throw runtime_error(
string(
"Missing time field representing the higher 32 bits. Expecting SCALARS with name " + it->first +
"_splitTime_high32 in the VTK file."));
1235 if(it->second.isLow32Found ==
false)
1237 throw runtime_error(
string(
"Missing time field representing the lower 32 bits. Expecting SCALARS with name " + it->first +
"_splitTime_low32 in the VTK file."));
1242 for(
int i=0; i<it->second.high32.cols(); i++)
1245 timeData(0,i) = (((std::int64_t) it->second.high32(0,i)) << 32) | ((std::int64_t) it->second.low32(0,i));
1248 loadedPoints.
addTime(it->first, timeData);
1251 return loadedPoints;
1261 template<
typename T>
1267 boost::assign::insert(param) (
"baseFileName",
"");
1268 boost::assign::insert(param) (
"writeBinary",
toParam(binary));
1269 boost::assign::insert(param) (
"precision",
toParam(precision));
1270 VTKInspector vtkInspector(param);
1271 vtkInspector.dumpDataPoints(
data, fileName);
1288 template<
typename T>
1291 ifstream ifs(fileName.c_str());
1293 throw runtime_error(
string(
"Cannot open file ") + fileName);
1304 template <
typename T>
1307 class Elements :
public vector<PLYElement*>{
1310 for (
typename vector<PLYElement*>::const_iterator it = this->begin(); it != this->
end(); it++ )
1339 bool format_defined =
false;
1340 bool header_processed =
false;
1345 bool skip_props =
false;
1346 unsigned elem_offset = 0;
1350 if (line.find(
"ply") != 0) {
1351 throw runtime_error(
string(
"PLY parse error: wrong magic header, found <") + line +
string(
">"));
1354 while (!header_processed)
1357 throw runtime_error(
"PLY parse error: reached end of file before end of header definition");
1362 istringstream stringstream (line);
1365 stringstream >> keyword;
1368 if (keyword ==
"comment") {
1373 if (keyword ==
"format")
1376 throw runtime_error(
"PLY parse error: format already defined");
1378 string format_str, version_str;
1379 stringstream >> format_str >> version_str;
1381 if (format_str !=
"ascii" && format_str !=
"binary_little_endian" && format_str !=
"binary_big_endian")
1382 throw runtime_error(
string(
"PLY parse error: format <") + format_str +
string(
"> is not supported"));
1384 if (format_str ==
"binary_little_endian" || format_str ==
"binary_big_endian")
1385 throw runtime_error(
string(
"PLY parse error: binary PLY files are not supported"));
1386 if (version_str !=
"1.0")
1388 throw runtime_error(
string(
"PLY parse error: version <") + version_str +
string(
"> of ply is not supported"));
1391 format_defined =
true;
1394 else if (keyword ==
"element")
1398 string elem_name, elem_num_s;
1399 stringstream >> elem_name >> elem_num_s;
1404 elem_num = boost::lexical_cast<unsigned>(elem_num_s);
1406 catch (boost::bad_lexical_cast&)
1408 throw runtime_error(
string(
"PLY parse error: bad number of elements ") + elem_num_s +
string(
" for element ") + elem_name);
1415 current_element = elem;
1418 for (
typename Elements::const_iterator it = elements.begin(); it != elements.end(); it++ )
1420 if (**it == *elem) {
1422 throw runtime_error(
string(
"PLY parse error: element: ") + elem_name +
string(
"is already defined"));
1425 elements.push_back(elem);
1430 LOG_WARNING_STREAM(
"PLY parse warning: element " << elem_name <<
" not supported. Skipping.");
1434 elem_offset += elem_num;
1436 else if (keyword ==
"property")
1438 if (current_element == NULL)
1440 throw runtime_error(
"PLY parse error: property listed without defining an element");
1446 string next, prop_type, prop_name;
1447 stringstream >> next;
1452 string prop_idx_type;
1453 stringstream >> prop_idx_type >> prop_type >> prop_name;
1457 current_element->
properties.push_back(list_prop);
1463 stringstream >> prop_name;
1471 else if (keyword ==
"end_header")
1473 if (!format_defined)
1475 throw runtime_error(
string(
"PLY parse error: format not defined in header"));
1478 if (elements.size() == 0)
1480 throw runtime_error(
string(
"PLY parse error: no elements defined in header"));
1483 header_processed =
true;
1493 if(vertex->
name !=
"vertex")
1495 throw runtime_error(
string(
"PLY parse error: vertex should be the first element defined."));
1501 int rowIdFeatures = 0;
1502 int rowIdDescriptors = 0;
1509 for(
size_t i=0; i<externalLabels.size(); i++)
1519 it->pmType = supLabel.
type;
1522 switch (supLabel.
type)
1525 it->pmRowID = rowIdFeatures;
1530 it->pmRowID = rowIdDescriptors;
1535 it->pmRowID = rowIdTime;
1539 throw runtime_error(
string(
"PLY Implementation Error: encounter a type different from FEATURE, DESCRIPTOR and TIME. Implementation not supported. See the definition of 'enum PMPropTypes'"));
1555 it->pmRowID = rowIdDescriptors;
1556 descLabelGen.
add(it->name);
1567 const unsigned int nbPoints = vertex->
num;
1577 const int nbValues = nbPoints*nbProp;
1580 for(
int i=0; i<nbValues; i++)
1585 throw runtime_error(
1586 (boost::format(
"PLY parse error: expected %1% values (%2% points with %3% properties) but only found %4% values.") % nbValues % nbPoints % nbProp % i).str());
1590 const int row = vertex->
properties[propID].pmRowID;
1602 features(row, col) = value;
1605 descriptors(row, col) = value;
1608 times(row, col) = value;
1611 throw runtime_error(
"Implementation error in loadPLY(). This should not throw.");
1617 if(propID >= nbProp)
1632 if (descriptors.rows() > 0)
1638 if(times.rows() > 0)
1640 loadedPoints.
times = times;
1647 loadedPoints.
addFeature(
"pad", Matrix::Ones(1,features.cols()));
1650 return loadedPoints;
1654 template<
typename T>
1660 ofstream ofs(fileName.c_str());
1662 throw runtime_error(
string(
"Cannot open file ") + fileName);
1664 ofs.precision(precision);
1665 const int pointCount(
data.features.cols());
1666 const int featCount(
data.features.rows());
1667 const int descRows(
data.descriptors.rows());
1670 if (pointCount == 0)
1676 ofs <<
"ply\n" <<
"format ascii 1.0\n";
1677 ofs <<
"element vertex " << pointCount <<
"\n";
1678 for (
int f=0; f <(featCount-1); f++)
1680 ofs <<
"property float " <<
data.featureLabels[f].text <<
"\n";
1683 for (
size_t i = 0; i <
data.descriptorLabels.size(); i++)
1686 for (
size_t s = 0; s < lab.
span; s++)
1689 ofs <<
"property float " <<
getColLabel(lab,s) <<
"\n";
1693 ofs <<
"end_header\n";
1696 for (
int p = 0; p < pointCount; ++p)
1698 for (
int f = 0; f < featCount - 1; ++f)
1700 ofs <<
data.features(f, p);
1701 if(!(f == featCount-2 && descRows == 0))
1705 bool datawithColor =
data.descriptorExists(
"color");
1706 int colorStartingRow =
data.getDescriptorStartingRow(
"color");
1707 int colorEndRow = colorStartingRow +
data.getDescriptorDimension(
"color");
1708 for (
int d = 0; d < descRows; ++d)
1710 if (datawithColor && d >= colorStartingRow && d < colorEndRow) {
1711 ofs << static_cast<unsigned>(
data.descriptors(d, p) * 255.0);
1713 ofs <<
data.descriptors(d, p);
1730 template<
typename T>
1743 throw std::runtime_error(
1755 template<
typename T>
1769 throw std::runtime_error(
1791 template <
typename T>
1794 string lc = elem_name;
1795 boost::algorithm::to_lower(lc);
1807 template <
typename T>
1814 template<
typename T>
1816 const std::string& elem_name,
const int elem_num,
const unsigned offset) {
1825 template<
typename T>
1827 return (type ==
"char" || type ==
"uchar" || type ==
"short"
1828 || type ==
"ushort" || type ==
"int" || type ==
"uint"
1829 || type ==
"float" || type ==
"double");
1833 template <
typename T>
1840 template <
typename T>
1849 template<
typename T>
1851 ifstream ifs(fileName.c_str());
1853 throw runtime_error(
string(
"Cannot open file ") + fileName);
1866 template<
typename T>
1912 if (line.substr(0,1) ==
"#" || line ==
"")
1918 vector<string> tokens;
1919 boost::split(tokens, line, boost::is_any_of(
"\t\r "), boost::token_compress_on);
1921 string pcd_version_str;
1922 if (tokens[0] ==
"VERSION")
1926 if (tokens[1] !=
"0.7" && tokens[1] !=
".7")
1927 throw runtime_error(
"PCD Parse Error: Only PCD Version 0.7 is supported");
1930 else if (tokens[0] ==
"FIELDS")
1934 for (
size_t i = 1; i < tokens.size(); i++)
1941 else if (tokens[0] ==
"SIZE")
1943 if((tokens.size() - 1) != header.
properties.size())
1944 throw runtime_error(
"PCD Parse Error: number of elements for SIZE must be the same as FIELDS");
1946 for (
size_t i = 1; i < tokens.size(); i++)
1948 const unsigned int size = boost::lexical_cast<unsigned int >(tokens[i]);
1954 else if (tokens[0] ==
"TYPE")
1956 if((tokens.size() - 1) != header.
properties.size())
1957 throw runtime_error(
"PCD Parse Error: number of elements for TYPE must be the same as FIELDS");
1959 for (
size_t i = 1; i < tokens.size(); i++)
1961 const char type = boost::lexical_cast<char>(tokens[i]);
1963 if (type !=
'I' && type !=
'U' && type !=
'F')
1964 throw runtime_error(
"PCD Parse Error: invalid TYPE, it must be 'I', 'U', or 'F'");
1969 else if (tokens[0] ==
"COUNT")
1972 if((tokens.size() - 1) != header.
properties.size())
1973 throw runtime_error(
"PCD Parse Error: number of elements for COUNT must be the same as FIELDS");
1975 for (
size_t i = 1; i < tokens.size(); i++)
1977 const unsigned int count = boost::lexical_cast<unsigned int >(tokens[i]);
1983 else if (tokens[0] ==
"WIDTH")
1987 header.
width = boost::lexical_cast<unsigned int >(tokens[1]);
1989 catch (boost::bad_lexical_cast&)
1991 throw runtime_error(
"PCD Parse Error: invalid WIDTH");
1996 else if (tokens[0] ==
"HEIGHT")
2000 header.
height= boost::lexical_cast<unsigned int >(tokens[1]);
2002 catch (boost::bad_lexical_cast&)
2004 throw runtime_error(
"PCD Parse Error: invalid HEIGHT");
2009 else if (tokens[0] ==
"VIEWPOINT")
2011 if((tokens.size() - 1) != 7 )
2012 throw runtime_error(
"PCD Parse Error: number of elements for VIEWPOINT must be 7");
2014 for (
size_t i = 1; i < tokens.size(); i++)
2018 header.
viewPoint(i-1, 0) = boost::lexical_cast<T>(tokens[i]);
2020 catch (boost::bad_lexical_cast&)
2023 ss <<
"PCD Parse Error: invalid value(" << tokens[i] <<
") of VIEWPOINT";
2024 throw runtime_error(ss.str());
2030 else if (tokens[0] ==
"POINTS")
2034 header.
nbPoints = boost::lexical_cast<unsigned int>(tokens[1]);
2036 catch (boost::bad_lexical_cast&)
2039 ss <<
"PCD Parse Error: the value in the element POINTS (" << tokens[1] <<
") could not be cast as unsigned int";
2040 throw runtime_error(ss.str());
2044 else if (tokens[0] ==
"DATA")
2053 else if(header.
dataType ==
"binary")
2055 throw runtime_error(
"PCD Implementation Error: the option for DATA binary is not implemented yet");
2060 ss <<
"PCD Parse Error: the value in the element DATA (" << tokens[1] <<
") must be ascii or binary";
2061 throw runtime_error(ss.str());
2070 throw runtime_error(
"PCD Parse Error: no FIELDS were find in the header");
2075 throw runtime_error(
"PCD Parse Error: POINTS field does not match WIDTH and HEIGHT fields");
2083 int rowIdFeatures = 0;
2084 int rowIdDescriptors = 0;
2090 for(
size_t i=0; i<externalLabels.size(); i++)
2095 for(
size_t i=0; i < header.
properties.size(); i++)
2106 switch (supLabel.
type)
2109 header.
properties[i].pmRowID = rowIdFeatures;
2115 ss <<
"PCD Parse Error: the field " << prop.
field <<
" must have a count of 1";
2116 throw runtime_error(ss.str());
2120 header.
properties[i].pmRowID = rowIdDescriptors;
2122 rowIdDescriptors += prop.
count;
2127 rowIdTime += prop.
count;
2129 throw runtime_error(
string(
"PCD Implementation Error: encounter a type different from FEATURE, DESCRIPTOR and TIME. Implementation not supported. See the definition of 'enum PMPropTypes'"));
2140 for(
size_t i=0; i < header.
properties.size(); i++)
2146 header.
properties[i].pmRowID = rowIdDescriptors;
2148 rowIdDescriptors += prop.
count;
2159 const unsigned int totalDim = featDim + descDim + timeDim;
2160 const unsigned int nbPoints = header.
nbPoints;
2178 if (line.substr(0,1) ==
"#" || line ==
"")
2184 vector<string> tokens;
2185 boost::split(tokens, line, boost::is_any_of(
"\t\r "), boost::token_compress_on);
2188 if (tokens.size() != totalDim)
2189 throw runtime_error(
string(
"PCD Parse Error: number of data columns does not match number of fields at line: ") + boost::lexical_cast<string>(lineNum));
2191 unsigned int fileCol = 0;
2192 for(
size_t i=0; i<header.
properties.size(); i++)
2194 const unsigned int count = header.
properties[i].count;
2195 const unsigned int row = header.
properties[i].pmRowID;
2199 for(
size_t j=0; j<count; j++)
2204 features(row+j, col) = boost::lexical_cast<T>(tokens[fileCol]);
2207 descriptors(row+j, col) = boost::lexical_cast<T>(tokens[fileCol]);
2210 times(row+j, col) = boost::lexical_cast<std::int64_t>(tokens[fileCol]);
2213 throw runtime_error(
"Implementation error in loadPCD(). This should not throw.");
2226 if (col != nbPoints)
2229 ss <<
"PCD Parse Error: the number of points in the file (" << col <<
") is less than the specified number of points (" << nbPoints <<
")";
2230 throw runtime_error(ss.str());
2238 if (descriptors.rows() > 0)
2244 if(times.rows() > 0)
2246 loadedPoints.
times = times;
2253 loadedPoints.
addFeature(
"pad", Matrix::Ones(1,features.cols()));
2256 return loadedPoints;
2259 template<
typename T>
2261 const std::string& fileName,
unsigned precision) {
2262 ofstream ofs(fileName.c_str());
2264 throw runtime_error(
string(
"Cannot open file ") + fileName);
2265 ofs.precision(precision);
2266 const int pointCount(
data.features.cols());
2267 const int featCount(
data.features.rows());
2268 const int descRows(
data.descriptors.rows());
2269 const int descCount(
data.descriptorLabels.size());
2271 if (pointCount == 0)
2277 ofs <<
"# .PCD v.7 - Point Cloud Data file format\n" <<
"VERSION .7\n";
2280 for (
int f=0; f < (featCount - 1); f++)
2282 ofs <<
" " <<
data.featureLabels[f].text;
2289 for (
int i = 0; i < descCount; i++)
2291 ofs <<
" " <<
data.descriptorLabels[i].text;
2297 for (
int i =0; i < featCount - 1 + descCount; i++)
2304 for (
int i =0; i < featCount - 1 + descCount; i++)
2311 for (
int f = 0; f < featCount - 1 ; f++ )
2317 for (
int i = 0; i < descCount; i++)
2319 ofs <<
" " <<
data.descriptorLabels[i].span;
2324 ofs <<
"WIDTH " << pointCount <<
"\n";
2325 ofs <<
"HEIGHT 1\n";
2326 ofs <<
"POINTS " << pointCount <<
"\n";
2327 ofs <<
"DATA ascii\n";
2330 for (
int p = 0; p < pointCount; ++p)
2332 for (
int f = 0; f < featCount - 1; ++f)
2334 ofs <<
data.features(f, p);
2335 if(!(f == featCount-2 && descRows == 0))
2338 for (
int d = 0; d < descRows; ++d)
2340 ofs <<
data.descriptors(d, p);