39 #include "boost/filesystem.hpp"
50 typedef PM::DataPoints
DP;
57 string& outputBaseFile,
58 string& initTranslation,
string& initRotation);
60 const int cloudDimension);
62 const int cloudDimension);
63 void usage(
const char *argv[]);
72 int main(
int argc,
const char *argv[])
74 bool isTransfoSaved =
false;
76 string outputBaseFile(
"test");
77 string initTranslation(
"0,0,0");
78 string initRotation(
"1,0,0;0,1,0;0,0,1");
79 const int ret =
validateArgs(argc, argv, isTransfoSaved, configFile,
80 outputBaseFile, initTranslation, initRotation);
85 const char *refFile(argv[argc-2]);
86 const char *dataFile(argv[argc-1]);
95 if (configFile.empty())
103 ifstream ifs(configFile.c_str());
106 cerr <<
"Cannot open config file " << configFile <<
", usage:";
usage(argv); exit(1);
108 icp.loadFromYaml(ifs);
113 if (!(cloudDimension == 2 || cloudDimension == 3))
115 cerr <<
"Invalid input point clouds dimension" << endl;
125 std::shared_ptr<PM::Transformation> rigidTrans;
126 rigidTrans =
PM::get().REG(Transformation).create(
"RigidTransformation");
128 if (!rigidTrans->checkParameters(initTransfo)) {
130 <<
"Initial transformation is not rigid, identiy will be used"
132 initTransfo = PM::TransformationParameters::Identity(
133 cloudDimension+1,cloudDimension+1);
136 const DP initializedData = rigidTrans->compute(data, initTransfo);
140 float matchRatio = icp.errorMinimizer->getWeightedPointUsedRatio();
141 cout <<
"match ratio: " << matchRatio << endl;
146 DP data_out(initializedData);
147 icp.transformations.apply(data_out, T);
149 cout << endl <<
"------------------" << endl;
166 params[
"epsilon"] =
toParam(0);
168 std::shared_ptr<PM::Matcher> matcherHausdorff =
PM::get().MatcherRegistrar.create(
"KDTreeMatcher", params);
171 matcherHausdorff->init(ref);
172 matches = matcherHausdorff->findClosests(data_out);
173 float maxDist1 = matches.getDistsQuantile(1.0);
174 float maxDistRobust1 = matches.getDistsQuantile(0.85);
177 matcherHausdorff->init(data_out);
178 matches = matcherHausdorff->findClosests(ref);
179 float maxDist2 = matches.getDistsQuantile(1.0);
180 float maxDistRobust2 = matches.getDistsQuantile(0.85);
182 float haussdorffDist = std::max(maxDist1, maxDist2);
183 float haussdorffQuantileDist = std::max(maxDistRobust1, maxDistRobust2);
185 cout <<
"Haussdorff distance: " << std::sqrt(haussdorffDist) <<
" m" << endl;
186 cout <<
"Haussdorff quantile distance: " << std::sqrt(haussdorffQuantileDist) <<
" m" << endl;
198 icp.matcher->init(ref);
201 matches = icp.matcher->findClosests(data_out);
204 const PM::OutlierWeights outlierWeights = icp.outlierFilters.compute(data_out, ref, matches);
207 const PM::ErrorMinimizer::ErrorElements matchedPoints( data_out, ref, outlierWeights, matches);
210 const int dim = matchedPoints.reading.getEuclideanDim();
211 const int nbMatchedPoints = matchedPoints.reading.getNbPoints();
212 const PM::Matrix matchedRead = matchedPoints.reading.features.topRows(dim);
213 const PM::Matrix matchedRef = matchedPoints.reference.features.topRows(dim);
216 const PM::Matrix dist = (matchedRead - matchedRef).colwise().norm();
217 const float meanDist = dist.sum()/nbMatchedPoints;
218 cout <<
"Robust mean distance: " << meanDist <<
" m" << endl;
222 cout <<
"------------------" << endl << endl;
226 ref.
save(outputBaseFile +
"_ref.vtk");
227 data.
save(outputBaseFile +
"_data_in.vtk");
228 data_out.
save(outputBaseFile +
"_data_out.vtk");
230 ofstream transfoFile;
231 string initFileName = outputBaseFile +
"_init_transfo.txt";
232 string icpFileName = outputBaseFile +
"_icp_transfo.txt";
233 string completeFileName = outputBaseFile +
"_complete_transfo.txt";
235 transfoFile.open(initFileName.c_str());
236 if(transfoFile.is_open()) {
237 transfoFile << initTransfo << endl;
240 cout <<
"Unable to write the initial transformation file\n" << endl;
243 transfoFile.open(icpFileName.c_str());
244 if(transfoFile.is_open()) {
245 transfoFile << T << endl;
248 cout <<
"Unable to write the ICP transformation file\n" << endl;
251 transfoFile.open(completeFileName.c_str());
252 if(transfoFile.is_open()) {
253 transfoFile << T*initTransfo << endl;
256 cout <<
"Unable to write the complete transformation file\n" << endl;
260 cout <<
"ICP transformation:" << endl << T << endl;
271 cout <<
"* " << name <<
" *\n" << endl;
272 for (BOOST_AUTO(it, registrar.begin()); it != registrar.end(); ++it)
274 cout << it->first << endl;
276 cout << it->second->availableParameters() << endl;
281 #define DUMP_REGISTRAR_CONTENT(pm, name, bib) \
282 dumpRegistrar(pm, pm.REG(name), # name, bib);
297 cout <<
"* Bibliography *" << endl << endl;
303 bool& isTransfoSaved,
305 string& outputBaseFile,
306 string& initTranslation,
string& initRotation)
310 cerr <<
"Not enough arguments, usage:";
316 if (
string(argv[1]) ==
"-l")
323 cerr <<
"Wrong option, usage:";
329 const int endOpt(argc - 2);
330 for (
int i = 1; i < endOpt; i += 2)
332 const string opt(argv[i]);
335 cerr <<
"Missing value for option " << opt <<
", usage:";
usage(argv); exit(1);
337 if (opt ==
"--isTransfoSaved") {
338 if (strcmp(argv[i+1],
"1") == 0 || strcmp(argv[i+1],
"true") == 0) {
339 isTransfoSaved =
true;
341 else if (strcmp(argv[i+1],
"0") == 0
342 || strcmp(argv[i+1],
"false") == 0) {
343 isTransfoSaved =
false;
346 cerr <<
"Invalid value for parameter isTransfoSaved." << endl
347 <<
"Value must be true or false or 1 or 0." << endl
348 <<
"Default value will be used." << endl;
351 else if (opt ==
"--config") {
352 configFile = argv[i+1];
354 else if (opt ==
"--output") {
355 outputBaseFile = argv[i+1];
357 else if (opt ==
"--initTranslation") {
358 initTranslation = argv[i+1];
360 else if (opt ==
"--initRotation") {
361 initRotation = argv[i+1];
365 cerr <<
"Unknown option " << opt <<
", usage:";
usage(argv); exit(1);
372 const int cloudDimension) {
374 parsedTranslation = PM::TransformationParameters::Identity(
375 cloudDimension+1,cloudDimension+1);
377 translation.erase(std::remove(translation.begin(), translation.end(),
'['),
379 translation.erase(std::remove(translation.begin(), translation.end(),
']'),
381 std::replace( translation.begin(), translation.end(),
',',
' ');
382 std::replace( translation.begin(), translation.end(),
';',
' ');
384 float translationValues[3] = {0};
385 stringstream translationStringStream(translation);
386 for(
int i = 0; i < cloudDimension; i++) {
387 if(!(translationStringStream >> translationValues[i])) {
388 cerr <<
"An error occured while trying to parse the initial "
389 <<
"translation." << endl
390 <<
"No initial translation will be used" << endl;
391 return parsedTranslation;
394 float extraOutput = 0;
395 if((translationStringStream >> extraOutput)) {
396 cerr <<
"Wrong initial translation size" << endl
397 <<
"No initial translation will be used" << endl;
398 return parsedTranslation;
401 for(
int i = 0; i < cloudDimension; i++) {
402 parsedTranslation(i,cloudDimension) = translationValues[i];
405 return parsedTranslation;
409 const int cloudDimension){
411 parsedRotation = PM::TransformationParameters::Identity(
412 cloudDimension+1,cloudDimension+1);
414 rotation.erase(std::remove(rotation.begin(), rotation.end(),
'['),
416 rotation.erase(std::remove(rotation.begin(), rotation.end(),
']'),
418 std::replace( rotation.begin(), rotation.end(),
',',
' ');
419 std::replace( rotation.begin(), rotation.end(),
';',
' ');
421 float rotationMatrix[9] = {0};
422 stringstream rotationStringStream(rotation);
423 for(
int i = 0; i < cloudDimension*cloudDimension; i++) {
424 if(!(rotationStringStream >> rotationMatrix[i])) {
425 cerr <<
"An error occured while trying to parse the initial "
426 <<
"rotation." << endl
427 <<
"No initial rotation will be used" << endl;
428 return parsedRotation;
431 float extraOutput = 0;
432 if((rotationStringStream >> extraOutput)) {
433 cerr <<
"Wrong initial rotation size" << endl
434 <<
"No initial rotation will be used" << endl;
435 return parsedRotation;
438 for(
int i = 0; i < cloudDimension*cloudDimension; i++) {
439 parsedRotation(i/cloudDimension,i%cloudDimension) = rotationMatrix[i];
442 return parsedRotation;
449 cerr << endl << endl;
450 cerr <<
"* To list modules:" << endl;
451 cerr <<
" " << argv[0] <<
" -l" << endl;
453 cerr <<
"* To run ICP:" << endl;
454 cerr <<
" " << argv[0] <<
" [OPTIONS] reference.csv reading.csv" << endl;
456 cerr <<
"OPTIONS can be a combination of:" << endl;
457 cerr <<
"--config YAML_CONFIG_FILE Load the config from a YAML file (default: default parameters)" << endl;
458 cerr <<
"--output BASEFILENAME Name of output files (default: test)" << endl;
459 cerr <<
"--initTranslation [x,y,z] Add an initial 3D translation before applying ICP (default: 0,0,0)" << endl;
460 cerr <<
"--initTranslation [x,y] Add an initial 2D translation before applying ICP (default: 0,0)" << endl;
461 cerr <<
"--initRotation [r00,r01,r02,r10,r11,r12,r20,r21,r22]" << endl;
462 cerr <<
" Add an initial 3D rotation before applying ICP (default: 1,0,0,0,1,0,0,0,1)" << endl;
463 cerr <<
"--initRotation [r00,r01,r10,r11]" << endl;
464 cerr <<
" Add an initial 2D rotation before applying ICP (default: 1,0,0,1)" << endl;
465 cerr <<
"--isTransfoSaved BOOL Save transformation matrix in three different files:" << endl;
466 cerr <<
" - BASEFILENAME_inti_transfo.txt" << endl;
467 cerr <<
" - BASEFILENAME_icp_transfo.txt" << endl;
468 cerr <<
" - BASEFILENAME_complete_transfo.txt" << endl;
469 cerr <<
" (default: false)" << endl;
471 cerr <<
"Running this program with a VTKFileInspector as Inspector will create three" << endl;
472 cerr <<
"vtk ouptput files: ./test_ref.vtk, ./test_data_in.vtk and ./test_data_out.vtk" << endl;
473 cerr << endl <<
"2D Example:" << endl;
474 cerr <<
" " << argv[0] <<
" ../examples/data/2D_twoBoxes.csv ../examples/data/2D_oneBox.csv" << endl;
475 cerr << endl <<
"3D Example:" << endl;
476 cerr <<
" " << argv[0] <<
" ../examples/data/car_cloud400.csv ../examples/data/car_cloud401.csv" << endl;