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);
63 const int oneBigEndian = isBigEndian ? 1 : 1 << 8 * (
sizeof(int) - 1);
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),
201 if (dataPath.empty())
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++)
263 info.
readingFileName = localToGlobalFileName(dataPath, readingFileNames[line]);
264 if (referenceFileNames)
265 info.
referenceFileName = localToGlobalFileName(dataPath, (*referenceFileNames)[line]);
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()));
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")
508 else if (label.
text ==
"eigVectors")
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;
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)
784 loadedPoints.descriptorLabels = descLabelGen.
getLabels();
789 loadedPoints.times = times;
790 loadedPoints.timeLabels = timeLabelGen.
getLabels();
794 if(!loadedPoints.featureExists(
"pad"))
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;
1404 catch (boost::bad_lexical_cast&)
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)
1633 loadedPoints.descriptorLabels = descLabelGen.
getLabels();
1636 if(times.rows() > 0)
1638 loadedPoints.times = times;
1639 loadedPoints.timeLabels = timeLabelGen.
getLabels();
1643 if(!loadedPoints.featureExists(
"pad"))
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++)
1677 ofs <<
"property float " << data.
featureLabels[f].text <<
"\n";
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(
1741 std::string(
"PLY parse error: property type ") + type
1752 template<
typename T>
1766 throw std::runtime_error(
1767 std::string(
"PLY parse error: property list type ") + idx_type
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>
1837 template <
typename T>
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")
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")
1931 for (
size_t i = 1; i < tokens.size(); 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++)
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++)
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++)
1980 else if (tokens[0] ==
"WIDTH")
1986 catch (boost::bad_lexical_cast&)
1988 throw runtime_error(
"PCD Parse Error: invalid WIDTH");
1993 else if (tokens[0] ==
"HEIGHT")
1999 catch (boost::bad_lexical_cast&)
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++)
2017 catch (boost::bad_lexical_cast&)
2020 ss <<
"PCD Parse Error: invalid value(" << tokens[i] <<
") of VIEWPOINT";
2021 throw runtime_error(ss.str());
2027 else if (tokens[0] ==
"POINTS")
2033 catch (boost::bad_lexical_cast&)
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")
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());
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;
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++)
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)
2238 loadedPoints.descriptorLabels = descLabelGen.
getLabels();
2241 if(times.rows() > 0)
2243 loadedPoints.times = times;
2244 loadedPoints.timeLabels = timeLabelGen.
getLabels();
2248 if(!loadedPoints.featureExists(
"pad"))
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)
int pmRowID
row id used in a DataPoints
TransformationParameters initialTransformation
matrix of initial estimate transform
bool is_list
member is true of property is a list
unsigned int count
number of dimension
void add(const std::string internalName)
add a name to the vector of labels. If already there, will increament the dimension.
bool findTransform(const PointMatcherSupport::CsvElements &data, const std::string &prefix, unsigned dim)
Return whether there is a valid transformation named prefix in data.
static DataPoints loadPCD(const std::string &fileName)
unsigned pos
index of the property in element
Matrix descriptors
descriptors of points in the cloud, might be empty
std::istream & readVtkData(bool readBinary, MatrixRef into, std::istream &in)
std::string toParam(const S &value)
Return the a string value using lexical_cast.
std::string referenceFileName
file name of the reference point cloud
std::string readingFileName
file name of the reading point cloud
void validateFile(const std::string &fileName)
Throw a runtime_error exception if fileName cannot be opened.
std::vector< SupportedLabel > SupportedLabels
Vector of supported labels in PointMatcher and their external names.
Eigen::Matrix< unsigned int, Eigen::Dynamic, Eigen::Dynamic > high32
Matrix containing file data representing the high 32 bits.
std::string configFileName
file name of the yaml configuration
Labels featureLabels
labels of features
std::string type
type of PLY property
Factory for PLY elements.
static bool plyPropTypeValid(const std::string &type)
Check that property defined by type is a valid PLY type note: type must be lowercase.
static void savePLY(const DataPoints &data, const std::string &fileName)
save datapoints to PLY point cloud format
static void saveVTK(const DataPoints &data, const std::string &fileName, bool binary=false)
Save point cloud to a file as VTK.
unsigned getDescriptorDimension(const std::string &name) const
Return the dimension of a descriptor with a given name. Return 0 if the name is not found...
std::string name
name identifying the PLY element
std::string name
name of PLY property
static std::vector< string > csvLineToVector(const char *line)
const bool isBigEndian
true if platform is big endian
The name for a certain number of dim.
std::string field
Name of the property.
void skipBlock(bool binary, int binarySize, std::istream &is, bool hasSeparateSizeParameter=true)
unsigned getDescriptorStartingRow(const std::string &name) const
Return the starting row of a descriptor with a given name. Return 0 if the name is not found...
static DataPoints loadCSV(const std::string &fileName)
Associate an external name to a DataPoints type of information.
PointMatcher< T >::TransformationParameters TransformationParameters
alias
std::map< std::string, std::vector< std::string > > CsvElements
Data from a CSV file.
PointMatcher< T >::Vector Vector
alias
PLYProperty()
Default constructor. If used member values must be filled later.
static DataPoints loadPLY(const std::string &fileName)
Load polygon file format (ply) file.
static const SupportedLabels & getSupportedExternalLabels()
bool operator==(const PLYElement &other) const
comparison operator for elements
PMPropTypes pmType
type of information in PointMatcher
PointMatcher< T >::DataPoints::Label Label
alias
unsigned num
number of occurences of the element
std::map< std::string, Parameter > Parameters
Parameters stored as a map of string->string.
PLYProperties properties
all properties found in the header
Functions and classes that are not dependant on scalar type are defined in this namespace.
static void saveCSV(const DataPoints &data, const std::string &fileName)
Save point cloud to a file as CSV.
Information for a PCD property.
PMPropTypes type
type of information in PointMatcher
PMPropTypes pmType
type of information in PointMatcher
bool operator==(const PLYProperty &other) const
list prop ctor
std::istream & safeGetLine(std::istream &is, std::string &t)
Replaces getline for handling windows style CR/LF line endings.
Functions and classes that are dependant on scalar type are defined in this templatized class...
PointMatcher< T >::Matrix Matrix
alias
Eigen::Matrix< unsigned int, Eigen::Dynamic, Eigen::Dynamic > low32
Matrix containing file data representing the low 32 bits.
std::string idx_type
for list properties, type of number of elements
bool descriptorExists(const std::string &name) const
Look if a descriptor with a given name exist.
static void savePCD(const DataPoints &data, const std::string &fileName)
save datapoints to PCD point cloud format
PointMatcher< T >::Int64Matrix Int64Matrix
alias
Labels getLabels() const
Return the vector of labels used to build a DataPoints.
Information to exploit a reading from a file using this library. Fields might be left blank if unused...
Target lexical_cast(const Source &arg)
General case of lexical cast, use boost.
unsigned total_props
total number of properties in PLY element
SupportedVTKDataTypes
Enumeration of legacy VTK data types that can be parsed.
#define LOG_WARNING_STREAM(args)
std::string localToGlobalFileName(const std::string &path, const std::string &fileName)
Join parentPath and fileName and return the result as a global path.
CsvElements parseCsvWithHeader(const std::string &fileName)
static ElementTypes getElementType(const std::string &elem_name)
const int oneBigEndian
is always a big endian independent of the platforms endianness
void addFeature(const std::string &name, const Matrix &newFeature)
Add a feature by name, remove first if already exists. The 'pad' field will stay at the end for homog...
Storage for time loaded separatly.
TransformationParameters getTransform(const PointMatcherSupport::CsvElements &data, const std::string &prefix, unsigned dim, unsigned line)
Return the transformation named prefix from data.
size_t span
number of data dimensions the label spans
TransformationParameters groundTruthTransformation
matrix of the ground-truth transform
static PLYElement * createElement(const std::string &elem_name, const int elem_num, const unsigned offset)
factory function, build element defined by name with elem_num elements
bool elementSupported(const std::string &elem_name)
returns true if element named elem_name is supported by this parser
static std::string getColLabel(const Label &label, const int row)
convert a descriptor label to an appropriate sub-label
void save(const std::string &fileName, bool binary=false) const
Save a point cloud to a file, determine format from extension.
Structure containing all information required to map external information to PointMatcher internal re...
A vector of file info, to be used in batch processing.
FileInfo(const std::string &readingPath="", const std::string &referencePath="", const std::string &configFileName="", const TransformationParameters &initialTransformation=TransformationParameters(), const TransformationParameters &groundTruthTransformation=TransformationParameters(), const Vector &gravity=Vector3::Zero())
Constructor, leave fields blank if unused.
Generate a vector of Labels by checking for collision is the same name is reused. ...
Interface for PLY property.
PLYProperties::iterator it_PLYProp
Iterator for a vector of PLY properties.
void addTime(const std::string &name, const Int64Matrix &newTime)
Add a time by name, remove first if already exists.
PMPropTypes
Type of information in a DataPoints. Each type is stored in its own dense matrix. ...
size_t totalDim() const
Return the sum of the spans of each label.
Matrix features
features of points in the cloud
static DataPoints load(const std::string &fileName)
Load a point cloud from a file, determine format from extension.
std::string internalName
name used in PointMatcher
void addDescriptor(const std::string &name, const Matrix &newDescriptor)
Add a descriptor by name, remove first if already exists.
Implementation of PLY vertex element.
std::string text
name of the label
Interface for all PLY elements.
std::string externalName
name used in external format
static DataPoints loadVTK(const std::string &fileName)
Load point cloud from a file as VTK.
Labels descriptorLabels
labels of descriptors
SupportedLabel(const std::string &internalName, const std::string &externalName, const PMPropTypes &type)
Constructor.