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]);
345 transformation(i,j) = value;
348 return transformation;
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;
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);
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);
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);
848 const int pointCount(data.
features.cols());
849 const int dimCount(data.
features.rows());
859 for (
int i = 0; i < dimCount - 1; i++)
863 if (!((i == (dimCount - 2)) && descDimCount == 0))
871 for (
size_t s = 0;
s < lab.
span;
s++)
874 if (n != (descDimCount - 1))
883 for (
int p = 0; p < pointCount; ++p)
885 for (
int i = 0; i < dimCount-1; ++i)
888 if(!((i == (dimCount - 2)) && descDimCount == 0))
892 for (
int i = 0; i < descDimCount; i++)
895 if (i != (descDimCount - 1))
912 ifstream ifs(fileName.c_str(), std::ios::binary);
914 throw runtime_error(
string(
"Cannot open file ") + fileName);
918 void skipBlock(
bool binary,
int binarySize, std::istream & is,
bool hasSeparateSizeParameter =
true){
923 throw std::runtime_error(
"File violates the VTK format : parameter 'n' is missing after a field name.");
926 if(hasSeparateSizeParameter) {
929 throw std::runtime_error(
"File violates the VTK format : parameter 'size' is missing after a field name.");
938 is.seekg(size * binarySize, std::ios_base::cur);
940 for (
int p = 0; p < n; p++)
951 std::map<std::string, SplitTime> labelledSplitTime;
958 if (line.find(
"# vtk DataFile Version") != 0)
959 throw runtime_error(
string(
"Wrong magic header, found ") + line);
963 const bool isBinary = (line ==
"BINARY");
964 if (line !=
"ASCII"){
966 throw runtime_error(
string(
"Wrong file type, expecting ASCII or BINARY, found ") + line);
972 if (line ==
"DATASET POLYDATA")
974 else if (line ==
"DATASET UNSTRUCTURED_GRID")
977 throw runtime_error(
string(
"Wrong data type, expecting DATASET POLYDATA, found ") + line);
987 while (is >> fieldName)
990 if(fieldName ==
"POINTS")
996 if(!(type ==
"float" || type ==
"double"))
997 throw runtime_error(
string(
"Field POINTS can only be of type double or float"));
999 Matrix features(4, pointCount);
1000 for (
int p = 0; p < pointCount; ++p)
1002 readVtkData(type, isBinary, features.template block<3, 1>(0, p), is);
1003 features(3, p) = 1.0;
1005 loadedPoints.
addFeature(
"x", features.row(0));
1006 loadedPoints.
addFeature(
"y", features.row(1));
1007 loadedPoints.
addFeature(
"z", features.row(2));
1008 loadedPoints.
addFeature(
"pad", features.row(3));
1014 else if(dataType ==
POLYDATA && fieldName ==
"VERTICES")
1019 else if(dataType ==
POLYDATA && fieldName ==
"LINES")
1024 else if(dataType ==
POLYDATA && fieldName ==
"POLYGONS")
1029 else if(dataType ==
POLYDATA && fieldName ==
"TRIANGLE_STRIPS")
1046 else if(fieldName ==
"POINT_DATA")
1048 int descriptorCount;
1049 is >> descriptorCount;
1050 if(pointCount != descriptorCount)
1051 throw runtime_error(
string(
"The size of POINTS is different than POINT_DATA"));
1055 else if (fieldName ==
"FIELD")
1057 string fieldDataName;
1059 is >> fieldDataName >> fieldDataCount;
1061 for (
int f = 0;
f < fieldDataCount;
f++)
1064 is >> name >> dim >> numTuples >> type;
1066 if(type ==
"vtkIdType")
1069 is.seekg(dim * numTuples * 4, std::ios_base::cur);
1072 for (
int t = 0;
t < dim * numTuples;
t++ )
1078 else if(!(type ==
"float" || type ==
"double"))
1079 throw runtime_error(
string(
"Field " + fieldName +
" is " + type +
" but can only be of type double or float"));
1082 Matrix descriptor(dim, pointCount);
1083 readVtkData(type, isBinary, descriptor.transpose(), is);
1087 else if(fieldName ==
"METADATA")
1091 while(!line.empty())
1102 bool isTimeSec =
false;
1103 bool isTimeNsec =
false;
1106 if(boost::algorithm::ends_with(name,
"_splitTime_high32"))
1109 boost::algorithm::erase_last(name,
"_splitTime_high32");
1112 if(boost::algorithm::ends_with(name,
"_splitTime_low32"))
1115 boost::algorithm::erase_last(name,
"_splitTime_low32");
1119 bool skipLookupTable =
false;
1120 bool isColorScalars =
false;
1121 if(fieldName ==
"SCALARS")
1125 skipLookupTable =
true;
1127 else if(fieldName ==
"VECTORS")
1132 else if(fieldName ==
"TENSORS")
1137 else if(fieldName ==
"NORMALS")
1142 else if(fieldName ==
"COLOR_SCALARS")
1146 isColorScalars =
true;
1149 throw runtime_error(
string(
"Unknown field name " + fieldName +
", expecting SCALARS, VECTORS, TENSORS, NORMALS or COLOR_SCALARS."));
1156 if(isTimeSec || isTimeNsec)
1164 typename std::map<std::string, SplitTime>::iterator it;
1166 it = labelledSplitTime.find(name);
1168 if(it == labelledSplitTime.end())
1171 t.high32 = Eigen::Matrix<unsigned int, Eigen::Dynamic, Eigen::Dynamic> (dim, pointCount);
1173 labelledSplitTime[name] =
t;
1179 assert(labelledSplitTime[name].isHigh32Found ==
false);
1180 readVtkData(type, isBinary, labelledSplitTime[name].high32.transpose(), is);
1181 labelledSplitTime[name].isHigh32Found =
true;
1187 assert(labelledSplitTime[name].isLow32Found ==
false);
1188 readVtkData(type, isBinary, labelledSplitTime[name].low32.transpose(), is);
1189 labelledSplitTime[name].isLow32Found =
true;
1195 Matrix descriptorData(dim, pointCount);
1197 if(isColorScalars && isBinary)
1199 std::vector<unsigned char> buffer(dim);
1200 for (
int i = 0; i < pointCount; ++i){
1201 is.read(
reinterpret_cast<char *
>(&buffer.front()), dim);
1202 for(
int r=0; r < dim; ++r){
1203 descriptorData(r, i) = buffer[r] /
static_cast<T
>(255.0);
1209 if(!(type ==
"float" || type ==
"double"))
1210 throw runtime_error(
string(
"Field " + fieldName +
" is " + type +
" but can only be of type double or float."));
1217 readVtkData(type, isBinary, descriptorData.transpose(), is);
1225 typename std::map<std::string, SplitTime>::iterator it;
1226 for(it=labelledSplitTime.begin(); it!=labelledSplitTime.end(); it++)
1229 if(it->second.isHigh32Found ==
false)
1231 throw runtime_error(
string(
"Missing time field representing the higher 32 bits. Expecting SCALARS with name " + it->first +
"_splitTime_high32 in the VTK file."));
1234 if(it->second.isLow32Found ==
false)
1236 throw runtime_error(
string(
"Missing time field representing the lower 32 bits. Expecting SCALARS with name " + it->first +
"_splitTime_low32 in the VTK file."));
1241 for(
int i=0; i<it->second.high32.cols(); i++)
1244 timeData(0,i) = (((std::int64_t) it->second.high32(0,i)) << 32) | ((std::int64_t) it->second.low32(0,i));
1247 loadedPoints.
addTime(it->first, timeData);
1250 return loadedPoints;
1260 template<
typename T>
1266 boost::assign::insert(
param) (
"baseFileName",
"");
1267 boost::assign::insert(
param) (
"writeBinary",
toParam(binary));
1268 VTKInspector vtkInspector(
param);
1269 vtkInspector.dumpDataPoints(data, fileName);
1286 template<
typename T>
1289 ifstream ifs(fileName.c_str());
1291 throw runtime_error(
string(
"Cannot open file ") + fileName);
1302 template <
typename T>
1305 class Elements :
public vector<PLYElement*>{
1308 for (
typename vector<PLYElement*>::const_iterator it = this->begin(); it != this->end(); it++ )
1337 bool format_defined =
false;
1338 bool header_processed =
false;
1343 bool skip_props =
false;
1344 unsigned elem_offset = 0;
1348 if (line.find(
"ply") != 0) {
1349 throw runtime_error(
string(
"PLY parse error: wrong magic header, found <") + line +
string(
">"));
1352 while (!header_processed)
1355 throw runtime_error(
"PLY parse error: reached end of file before end of header definition");
1360 istringstream stringstream (line);
1363 stringstream >> keyword;
1366 if (keyword ==
"comment") {
1371 if (keyword ==
"format")
1374 throw runtime_error(
"PLY parse error: format already defined");
1376 string format_str, version_str;
1377 stringstream >> format_str >> version_str;
1379 if (format_str !=
"ascii" && format_str !=
"binary_little_endian" && format_str !=
"binary_big_endian")
1380 throw runtime_error(
string(
"PLY parse error: format <") + format_str +
string(
"> is not supported"));
1382 if (format_str ==
"binary_little_endian" || format_str ==
"binary_big_endian")
1383 throw runtime_error(
string(
"PLY parse error: binary PLY files are not supported"));
1384 if (version_str !=
"1.0")
1386 throw runtime_error(
string(
"PLY parse error: version <") + version_str +
string(
"> of ply is not supported"));
1389 format_defined =
true;
1392 else if (keyword ==
"element")
1396 string elem_name, elem_num_s;
1397 stringstream >> elem_name >> elem_num_s;
1402 elem_num = boost::lexical_cast<unsigned>(elem_num_s);
1404 catch (boost::bad_lexical_cast& e)
1406 throw runtime_error(
string(
"PLY parse error: bad number of elements ") + elem_num_s +
string(
" for element ") + elem_name);
1413 current_element = elem;
1416 for (
typename Elements::const_iterator it = elements.begin(); it != elements.end(); it++ )
1418 if (**it == *elem) {
1420 throw runtime_error(
string(
"PLY parse error: element: ") + elem_name +
string(
"is already defined"));
1423 elements.push_back(elem);
1428 LOG_WARNING_STREAM(
"PLY parse warning: element " << elem_name <<
" not supported. Skipping.");
1432 elem_offset += elem_num;
1434 else if (keyword ==
"property")
1436 if (current_element == NULL)
1438 throw runtime_error(
"PLY parse error: property listed without defining an element");
1444 string next, prop_type, prop_name;
1445 stringstream >> next;
1450 string prop_idx_type;
1451 stringstream >> prop_idx_type >> prop_type >> prop_name;
1455 current_element->
properties.push_back(list_prop);
1461 stringstream >> prop_name;
1469 else if (keyword ==
"end_header")
1471 if (!format_defined)
1473 throw runtime_error(
string(
"PLY parse error: format not defined in header"));
1476 if (elements.size() == 0)
1478 throw runtime_error(
string(
"PLY parse error: no elements defined in header"));
1481 header_processed =
true;
1491 if(vertex->
name !=
"vertex")
1493 throw runtime_error(
string(
"PLY parse error: vertex should be the first element defined."));
1499 int rowIdFeatures = 0;
1500 int rowIdDescriptors = 0;
1507 for(
size_t i=0; i<externalLabels.size(); i++)
1517 it->pmType = supLabel.
type;
1520 switch (supLabel.
type)
1523 it->pmRowID = rowIdFeatures;
1528 it->pmRowID = rowIdDescriptors;
1533 it->pmRowID = rowIdTime;
1537 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'"));
1553 it->pmRowID = rowIdDescriptors;
1554 descLabelGen.
add(it->name);
1565 const unsigned int nbPoints = vertex->
num;
1575 const int nbValues = nbPoints*nbProp;
1578 for(
int i=0; i<nbValues; i++)
1583 throw runtime_error(
1584 (boost::format(
"PLY parse error: expected %1% values (%2% points with %3% properties) but only found %4% values.") % nbValues % nbPoints % nbProp % i).str());
1588 const int row = vertex->
properties[propID].pmRowID;
1600 features(row, col) = value;
1603 descriptors(row, col) = value;
1606 times(row, col) = value;
1609 throw runtime_error(
"Implementation error in loadPLY(). This should not throw.");
1615 if(propID >= nbProp)
1630 if (descriptors.rows() > 0)
1636 if(times.rows() > 0)
1638 loadedPoints.
times = times;
1645 loadedPoints.
addFeature(
"pad", Matrix::Ones(1,features.cols()));
1648 return loadedPoints;
1652 template<
typename T>
1658 ofstream ofs(fileName.c_str());
1660 throw runtime_error(
string(
"Cannot open file ") + fileName);
1662 const int pointCount(data.
features.cols());
1663 const int featCount(data.
features.rows());
1667 if (pointCount == 0)
1673 ofs <<
"ply\n" <<
"format ascii 1.0\n";
1674 ofs <<
"element vertex " << pointCount <<
"\n";
1675 for (
int f=0;
f <(featCount-1);
f++)
1683 for (
size_t s = 0;
s < lab.
span;
s++)
1686 ofs <<
"property float " <<
getColLabel(lab,
s) <<
"\n";
1690 ofs <<
"end_header\n";
1693 for (
int p = 0; p < pointCount; ++p)
1695 for (
int f = 0;
f < featCount - 1; ++
f)
1698 if(!(
f == featCount-2 && descRows == 0))
1705 for (
int d = 0;
d < descRows; ++
d)
1707 if (datawithColor &&
d >= colorStartingRow &&
d < colorEndRow) {
1708 ofs << static_cast<unsigned>(data.
descriptors(
d, p) * 255.0);
1727 template<
typename T>
1740 throw std::runtime_error(
1752 template<
typename T>
1766 throw std::runtime_error(
1788 template <
typename T>
1791 string lc = elem_name;
1792 boost::algorithm::to_lower(lc);
1804 template <
typename T>
1811 template<
typename T>
1813 const std::string& elem_name,
const int elem_num,
const unsigned offset) {
1822 template<
typename T>
1824 return (type ==
"char" || type ==
"uchar" || type ==
"short"
1825 || type ==
"ushort" || type ==
"int" || type ==
"uint"
1826 || type ==
"float" || type ==
"double");
1830 template <
typename T>
1833 return name == rhs.
name;
1837 template <
typename T>
1840 return name == rhs.
name && type == rhs.
type;
1846 template<
typename T>
1848 ifstream ifs(fileName.c_str());
1850 throw runtime_error(
string(
"Cannot open file ") + fileName);
1863 template<
typename T>
1909 if (line.substr(0,1) ==
"#" || line ==
"")
1915 vector<string> tokens;
1916 boost::split(tokens, line, boost::is_any_of(
"\t\r "), boost::token_compress_on);
1918 string pcd_version_str;
1919 if (tokens[0] ==
"VERSION")
1921 header.version = tokens[1];
1923 if (tokens[1] !=
"0.7" && tokens[1] !=
".7")
1924 throw runtime_error(
"PCD Parse Error: Only PCD Version 0.7 is supported");
1927 else if (tokens[0] ==
"FIELDS")
1929 header.properties.resize(tokens.size() - 1);
1931 for (
size_t i = 1; i < tokens.size(); i++)
1933 header.properties[i-1].field = tokens[i];
1938 else if (tokens[0] ==
"SIZE")
1940 if((tokens.size() - 1) !=
header.properties.size())
1941 throw runtime_error(
"PCD Parse Error: number of elements for SIZE must be the same as FIELDS");
1943 for (
size_t i = 1; i < tokens.size(); i++)
1945 const unsigned int size = boost::lexical_cast<unsigned int >(tokens[i]);
1946 header.properties[i-1].size = size;
1951 else if (tokens[0] ==
"TYPE")
1953 if((tokens.size() - 1) !=
header.properties.size())
1954 throw runtime_error(
"PCD Parse Error: number of elements for TYPE must be the same as FIELDS");
1956 for (
size_t i = 1; i < tokens.size(); i++)
1958 const char type = boost::lexical_cast<char>(tokens[i]);
1959 header.properties[i-1].type = type;
1960 if (type !=
'I' && type !=
'U' && type !=
'F')
1961 throw runtime_error(
"PCD Parse Error: invalid TYPE, it must be 'I', 'U', or 'F'");
1966 else if (tokens[0] ==
"COUNT")
1969 if((tokens.size() - 1) !=
header.properties.size())
1970 throw runtime_error(
"PCD Parse Error: number of elements for COUNT must be the same as FIELDS");
1972 for (
size_t i = 1; i < tokens.size(); i++)
1974 const unsigned int count = boost::lexical_cast<unsigned int >(tokens[i]);
1975 header.properties[i-1].count = count;
1980 else if (tokens[0] ==
"WIDTH")
1984 header.width = boost::lexical_cast<unsigned int >(tokens[1]);
1986 catch (boost::bad_lexical_cast& e)
1988 throw runtime_error(
"PCD Parse Error: invalid WIDTH");
1993 else if (tokens[0] ==
"HEIGHT")
1997 header.height= boost::lexical_cast<unsigned int >(tokens[1]);
1999 catch (boost::bad_lexical_cast& e)
2001 throw runtime_error(
"PCD Parse Error: invalid HEIGHT");
2006 else if (tokens[0] ==
"VIEWPOINT")
2008 if((tokens.size() - 1) != 7 )
2009 throw runtime_error(
"PCD Parse Error: number of elements for VIEWPOINT must be 7");
2011 for (
size_t i = 1; i < tokens.size(); i++)
2015 header.viewPoint(i-1, 0) = boost::lexical_cast<T>(tokens[i]);
2017 catch (boost::bad_lexical_cast& e)
2020 ss <<
"PCD Parse Error: invalid value(" << tokens[i] <<
") of VIEWPOINT";
2021 throw runtime_error(ss.str());
2027 else if (tokens[0] ==
"POINTS")
2031 header.nbPoints = boost::lexical_cast<unsigned int>(tokens[1]);
2033 catch (boost::bad_lexical_cast& e)
2036 ss <<
"PCD Parse Error: the value in the element POINTS (" << tokens[1] <<
") could not be cast as unsigned int";
2037 throw runtime_error(ss.str());
2041 else if (tokens[0] ==
"DATA")
2043 header.dataType= tokens[1];
2045 if (
header.dataType ==
"ascii")
2050 else if(
header.dataType ==
"binary")
2052 throw runtime_error(
"PCD Implementation Error: the option for DATA binary is not implemented yet");
2057 ss <<
"PCD Parse Error: the value in the element DATA (" << tokens[1] <<
") must be ascii or binary";
2058 throw runtime_error(ss.str());
2066 if (
header.properties.size() == 0)
2067 throw runtime_error(
"PCD Parse Error: no FIELDS were find in the header");
2072 throw runtime_error(
"PCD Parse Error: POINTS field does not match WIDTH and HEIGHT fields");
2080 int rowIdFeatures = 0;
2081 int rowIdDescriptors = 0;
2087 for(
size_t i=0; i<externalLabels.size(); i++)
2092 for(
size_t i=0; i <
header.properties.size(); i++)
2103 switch (supLabel.
type)
2106 header.properties[i].pmRowID = rowIdFeatures;
2112 ss <<
"PCD Parse Error: the field " << prop.
field <<
" must have a count of 1";
2113 throw runtime_error(ss.str());
2117 header.properties[i].pmRowID = rowIdDescriptors;
2119 rowIdDescriptors += prop.
count;
2122 header.properties[i].pmRowID = rowIdTime;
2124 rowIdTime += prop.
count;
2126 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'"));
2137 for(
size_t i=0; i <
header.properties.size(); i++)
2143 header.properties[i].pmRowID = rowIdDescriptors;
2145 rowIdDescriptors += prop.
count;
2156 const unsigned int totalDim = featDim + descDim + timeDim;
2157 const unsigned int nbPoints =
header.nbPoints;
2175 if (line.substr(0,1) ==
"#" || line ==
"")
2181 vector<string> tokens;
2182 boost::split(tokens, line, boost::is_any_of(
"\t\r "), boost::token_compress_on);
2185 if (tokens.size() != totalDim)
2186 throw runtime_error(
string(
"PCD Parse Error: number of data columns does not match number of fields at line: ") + boost::lexical_cast<string>(lineNum));
2188 unsigned int fileCol = 0;
2189 for(
size_t i=0; i<
header.properties.size(); i++)
2191 const unsigned int count =
header.properties[i].count;
2192 const unsigned int row =
header.properties[i].pmRowID;
2196 for(
size_t j=0; j<count; j++)
2201 features(row+j, col) = boost::lexical_cast<T>(tokens[fileCol]);
2204 descriptors(row+j, col) = boost::lexical_cast<T>(tokens[fileCol]);
2207 times(row+j, col) = boost::lexical_cast<std::int64_t>(tokens[fileCol]);
2210 throw runtime_error(
"Implementation error in loadPCD(). This should not throw.");
2223 if (col != nbPoints)
2226 ss <<
"PCD Parse Error: the number of points in the file (" << col <<
") is less than the specified number of points (" << nbPoints <<
")";
2227 throw runtime_error(ss.str());
2235 if (descriptors.rows() > 0)
2241 if(times.rows() > 0)
2243 loadedPoints.
times = times;
2250 loadedPoints.
addFeature(
"pad", Matrix::Ones(1,features.cols()));
2253 return loadedPoints;
2256 template<
typename T>
2259 ofstream ofs(fileName.c_str());
2261 throw runtime_error(
string(
"Cannot open file ") + fileName);
2263 const int pointCount(data.
features.cols());
2264 const int featCount(data.
features.rows());
2268 if (pointCount == 0)
2274 ofs <<
"# .PCD v.7 - Point Cloud Data file format\n" <<
"VERSION .7\n";
2277 for (
int f=0;
f < (featCount - 1);
f++)
2286 for (
int i = 0; i < descCount; i++)
2294 for (
int i =0; i < featCount - 1 + descCount; i++)
2301 for (
int i =0; i < featCount - 1 + descCount; i++)
2308 for (
int f = 0;
f < featCount - 1 ;
f++ )
2314 for (
int i = 0; i < descCount; i++)
2321 ofs <<
"WIDTH " << pointCount <<
"\n";
2322 ofs <<
"HEIGHT 1\n";
2323 ofs <<
"POINTS " << pointCount <<
"\n";
2324 ofs <<
"DATA ascii\n";
2327 for (
int p = 0; p < pointCount; ++p)
2329 for (
int f = 0;
f < featCount - 1; ++
f)
2332 if(!(
f == featCount-2 && descRows == 0))
2335 for (
int d = 0;
d < descRows; ++
d)