examples/icp.cpp
Go to the documentation of this file.
1 // kate: replace-tabs off; indent-width 4; indent-mode normal
2 // vim: ts=4:sw=4:noexpandtab
3 /*
4 
5 Copyright (c) 2010--2012,
6 François Pomerleau and Stephane Magnenat, ASL, ETHZ, Switzerland
7 You can contact the authors at <f dot pomerleau at gmail dot com> and
8 <stephane at magnenat dot net>
9 
10 All rights reserved.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright
15  notice, this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright
17  notice, this list of conditions and the following disclaimer in the
18  documentation and/or other materials provided with the distribution.
19  * Neither the name of the <organization> nor the
20  names of its contributors may be used to endorse or promote products
21  derived from this software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
24 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL ETH-ASL BE LIABLE FOR ANY
27 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
38 
39 #include <cassert>
40 #include <fstream>
41 #include <iostream>
42 #include <algorithm>
43 
44 using namespace std;
45 
50 
51 void listModules();
52 int validateArgs(const int argc, const char *argv[],
53  bool& isVerbose,
54  bool& isTransfoSaved,
55  string& configFile,
56  string& outputBaseFile,
57  string& initTranslation, string& initRotation);
59  const int cloudDimension);
61  const int cloudDimension);
62 // Helper functions
63 void usage(const char *argv[]);
64 
72 int main(int argc, const char *argv[])
73 {
74  bool isTransfoSaved = false;
75  bool isVerbose = false;
76  string configFile;
77  string outputBaseFile("test");
78  string initTranslation("0,0,0");
79  string initRotation("1,0,0;0,1,0;0,0,1");
80  const int ret = validateArgs(argc, argv, isVerbose, isTransfoSaved, configFile,
81  outputBaseFile, initTranslation, initRotation);
82  if (ret != 0)
83  {
84  return ret;
85  }
86  const char *refFile(argv[argc-2]);
87  const char *dataFile(argv[argc-1]);
88 
89  // Load point clouds
90  const DP ref(DP::load(refFile));
91  const DP data(DP::load(dataFile));
92 
93  // Create the default ICP algorithm
94  PM::ICP icp;
95 
96 
97  if (configFile.empty())
98  {
99  // See the implementation of setDefault() to create a custom ICP algorithm
100  icp.setDefault();
101  }
102  else
103  {
104  // load YAML config
105  ifstream ifs(configFile.c_str());
106  if (!ifs.good())
107  {
108  cerr << "Cannot open config file " << configFile << ", usage:"; usage(argv); exit(1);
109  }
110  icp.loadFromYaml(ifs);
111  }
112 
113 
114  int cloudDimension = ref.getEuclideanDim();
115 
116  if (!(cloudDimension == 2 || cloudDimension == 3))
117  {
118  cerr << "Invalid input point clouds dimension" << endl;
119  exit(1);
120  }
121 
122 
123 
125  parseTranslation(initTranslation, cloudDimension);
127  parseRotation(initRotation, cloudDimension);
129 
130  std::shared_ptr<PM::Transformation> rigidTrans;
131  rigidTrans = PM::get().REG(Transformation).create("RigidTransformation");
132 
133  if (!rigidTrans->checkParameters(initTransfo)) {
134  cerr << endl
135  << "Initial transformation is not rigid, identiy will be used"
136  << endl;
137  initTransfo = PM::TransformationParameters::Identity(
138  cloudDimension+1,cloudDimension+1);
139  }
140 
141  const DP initializedData = rigidTrans->compute(data, initTransfo);
142 
143  // Compute the transformation to express data in ref
144  PM::TransformationParameters T = icp(initializedData, ref);
145  if(isVerbose)
146  cout << "match ratio: " << icp.errorMinimizer->getWeightedPointUsedRatio() << endl;
147 
148  // Transform data to express it in ref
149  DP data_out(initializedData);
150  icp.transformations.apply(data_out, T);
151 
152  // Safe files to see the results
153  ref.save(outputBaseFile + "_ref.vtk");
154  data.save(outputBaseFile + "_data_in.vtk");
155  data_out.save(outputBaseFile + "_data_out.vtk");
156  if(isTransfoSaved) {
157  ofstream transfoFile;
158  string initFileName = outputBaseFile + "_init_transfo.txt";
159  string icpFileName = outputBaseFile + "_icp_transfo.txt";
160  string completeFileName = outputBaseFile + "_complete_transfo.txt";
161 
162  transfoFile.open(initFileName.c_str());
163  if(transfoFile.is_open()) {
164  transfoFile << initTransfo << endl;
165  transfoFile.close();
166  } else {
167  cerr << "Unable to write the initial transformation file\n" << endl;
168  }
169 
170  transfoFile.open(icpFileName.c_str());
171  if(transfoFile.is_open()) {
172  transfoFile << T << endl;
173  transfoFile.close();
174  } else {
175  cerr << "Unable to write the ICP transformation file\n" << endl;
176  }
177 
178  transfoFile.open(completeFileName.c_str());
179  if(transfoFile.is_open()) {
180  transfoFile << T*initTransfo << endl;
181  transfoFile.close();
182  } else {
183  cerr << "Unable to write the complete transformation file\n" << endl;
184  }
185  }
186  else
187  {
188  if(isVerbose)
189  cout << "ICP transformation:" << endl << T << endl;
190  }
191 
192  return 0;
193 }
194 
195 // The following code allows to dump all existing modules
196 template<typename R>
197 void dumpRegistrar(const PM& pm, const R& registrar, const std::string& name,
198  CurrentBibliography& bib)
199 {
200  cout << "* " << name << " *\n" << endl;
201  for (BOOST_AUTO(it, registrar.begin()); it != registrar.end(); ++it)
202  {
203  cout << it->first << endl;
204  cout << getAndReplaceBibEntries(it->second->description(), bib) << endl;
205  cout << it->second->availableParameters() << endl;
206  }
207  cout << endl;
208 }
209 
210 #define DUMP_REGISTRAR_CONTENT(pm, name, bib) \
211  dumpRegistrar(pm, pm.REG(name), # name, bib);
212 
214 {
216 
225 
226  cout << "* Bibliography *" << endl << endl;
227  bib.dump(cout);
228 }
229 
230 // Make sure that the command arguments make sense
231 int validateArgs(const int argc, const char *argv[],
232  bool& isVerbose,
233  bool& isTransfoSaved,
234  string& configFile,
235  string& outputBaseFile,
236  string& initTranslation, string& initRotation)
237 {
238  if (argc == 1)
239  {
240  cerr << "Not enough arguments, usage:";
241  usage(argv);
242  return 1;
243  }
244  else if (argc == 2)
245  {
246  if (string(argv[1]) == "-l")
247  {
248  listModules();
249  return -1; // we use -1 to say that we wish to quit but in a normal way
250  }
251  else
252  {
253  cerr << "Wrong option, usage:";
254  usage(argv);
255  return 2;
256  }
257  }
258 
259  const int endOpt(argc - 2);
260  for (int i = 1; i < endOpt; i += 2)
261  {
262  const string opt(argv[i]);
263  if (opt == "--verbose" || opt == "-v") {
264  isVerbose = true;
265  i --;
266  continue;
267  }
268  if (i + 1 > endOpt)
269  {
270  cerr << "Missing value for option " << opt << ", usage:"; usage(argv); exit(1);
271  }
272  if (opt == "--isTransfoSaved") {
273  if (strcmp(argv[i+1], "1") == 0 || strcmp(argv[i+1], "true") == 0) {
274  isTransfoSaved = true;
275  }
276  else if (strcmp(argv[i+1], "0") == 0
277  || strcmp(argv[i+1],"false") == 0) {
278  isTransfoSaved = false;
279  }
280  else {
281  cerr << "Invalid value for parameter isTransfoSaved." << endl
282  << "Value must be true or false or 1 or 0." << endl
283  << "Default value will be used." << endl;
284  }
285  }
286  else if (opt == "--config") {
287  configFile = argv[i+1];
288  }
289  else if (opt == "--output") {
290  outputBaseFile = argv[i+1];
291  }
292  else if (opt == "--initTranslation") {
293  initTranslation = argv[i+1];
294  }
295  else if (opt == "--initRotation") {
296  initRotation = argv[i+1];
297  }
298  else
299  {
300  cerr << "Unknown option " << opt << ", usage:"; usage(argv); exit(1);
301  }
302  }
303  return 0;
304 }
305 
307  const int cloudDimension) {
308  PM::TransformationParameters parsedTranslation;
309  parsedTranslation = PM::TransformationParameters::Identity(
310  cloudDimension+1,cloudDimension+1);
311 
312  translation.erase(std::remove(translation.begin(), translation.end(), '['),
313  translation.end());
314  translation.erase(std::remove(translation.begin(), translation.end(), ']'),
315  translation.end());
316  std::replace( translation.begin(), translation.end(), ',', ' ');
317  std::replace( translation.begin(), translation.end(), ';', ' ');
318 
319  float translationValues[3] = {0};
320  stringstream translationStringStream(translation);
321  for( int i = 0; i < cloudDimension; i++) {
322  if(!(translationStringStream >> translationValues[i])) {
323  cerr << "An error occured while trying to parse the initial "
324  << "translation." << endl
325  << "No initial translation will be used" << endl;
326  return parsedTranslation;
327  }
328  }
329  float extraOutput = 0;
330  if((translationStringStream >> extraOutput)) {
331  cerr << "Wrong initial translation size" << endl
332  << "No initial translation will be used" << endl;
333  return parsedTranslation;
334  }
335 
336  for( int i = 0; i < cloudDimension; i++) {
337  parsedTranslation(i,cloudDimension) = translationValues[i];
338  }
339 
340  return parsedTranslation;
341 }
342 
344  const int cloudDimension){
345  PM::TransformationParameters parsedRotation;
346  parsedRotation = PM::TransformationParameters::Identity(
347  cloudDimension+1,cloudDimension+1);
348 
349  rotation.erase(std::remove(rotation.begin(), rotation.end(), '['),
350  rotation.end());
351  rotation.erase(std::remove(rotation.begin(), rotation.end(), ']'),
352  rotation.end());
353  std::replace( rotation.begin(), rotation.end(), ',', ' ');
354  std::replace( rotation.begin(), rotation.end(), ';', ' ');
355 
356  float rotationMatrix[9] = {0};
357  stringstream rotationStringStream(rotation);
358  for( int i = 0; i < cloudDimension*cloudDimension; i++) {
359  if(!(rotationStringStream >> rotationMatrix[i])) {
360  cerr << "An error occured while trying to parse the initial "
361  << "rotation." << endl
362  << "No initial rotation will be used" << endl;
363  return parsedRotation;
364  }
365  }
366  float extraOutput = 0;
367  if((rotationStringStream >> extraOutput)) {
368  cerr << "Wrong initial rotation size" << endl
369  << "No initial rotation will be used" << endl;
370  return parsedRotation;
371  }
372 
373  for( int i = 0; i < cloudDimension*cloudDimension; i++) {
374  parsedRotation(i/cloudDimension,i%cloudDimension) = rotationMatrix[i];
375  }
376 
377  return parsedRotation;
378 }
379 
380 // Dump command-line help
381 void usage(const char *argv[])
382 {
383  //TODO: add new options --isTransfoSaved, --initTranslation, --initRotation
384  cerr << endl << endl;
385  cerr << "* To list modules:" << endl;
386  cerr << " " << argv[0] << " -l" << endl;
387  cerr << endl;
388  cerr << "* To run ICP:" << endl;
389  cerr << " " << argv[0] << " [OPTIONS] reference.csv reading.csv" << endl;
390  cerr << endl;
391  cerr << "OPTIONS can be a combination of:" << endl;
392  cerr << "-v,--verbose Be more verbose (info logging to stdout)" << endl;
393  cerr << "--config YAML_CONFIG_FILE Load the config from a YAML file (default: default parameters)" << endl;
394  cerr << "--output BASEFILENAME Name of output files (default: test)" << endl;
395  cerr << "--initTranslation [x,y,z] Add an initial 3D translation before applying ICP (default: 0,0,0)" << endl;
396  cerr << "--initTranslation [x,y] Add an initial 2D translation before applying ICP (default: 0,0)" << endl;
397  cerr << "--initRotation [r00,r01,r02,r10,r11,r12,r20,r21,r22]" << endl;
398  cerr << " Add an initial 3D rotation before applying ICP (default: 1,0,0,0,1,0,0,0,1)" << endl;
399  cerr << "--initRotation [r00,r01,r10,r11]" << endl;
400  cerr << " Add an initial 2D rotation before applying ICP (default: 1,0,0,1)" << endl;
401  cerr << "--isTransfoSaved BOOL Save transformation matrix in three different files:" << endl;
402  cerr << " - BASEFILENAME_inti_transfo.txt" << endl;
403  cerr << " - BASEFILENAME_icp_transfo.txt" << endl;
404  cerr << " - BASEFILENAME_complete_transfo.txt" << endl;
405  cerr << " (default: false)" << endl;
406  cerr << endl;
407  cerr << "Running this program with a VTKFileInspector as Inspector will create three" << endl;
408  cerr << "vtk ouptput files: ./test_ref.vtk, ./test_data_in.vtk and ./test_data_out.vtk" << endl;
409  cerr << endl << "2D Example:" << endl;
410  cerr << " " << argv[0] << " ../examples/data/2D_twoBoxes.csv ../examples/data/2D_oneBox.csv" << endl;
411  cerr << endl << "3D Example:" << endl;
412  cerr << " " << argv[0] << " ../examples/data/car_cloud400.csv ../examples/data/car_cloud401.csv" << endl;
413  cerr << endl;
414 }
415 
416 
417 
DP
PM::DataPoints DP
Definition: examples/icp.cpp:47
PointMatcher< float >::Parameters
Parametrizable::Parameters Parameters
alias
Definition: PointMatcher.h:186
listModules
void listModules()
Definition: examples/icp.cpp:213
DataPointsFilter
PM::DataPointsFilter DataPointsFilter
Definition: pypoint_matcher_helper.h:22
PM
PointMatcher< float > PM
Definition: examples/icp.cpp:46
build_map.T
T
Definition: build_map.py:34
PointMatcherSupport::getAndReplaceBibEntries
std::string getAndReplaceBibEntries(const std::string &, CurrentBibliography &curBib)
DataPoints
PM::DataPoints DataPoints
Definition: pypoint_matcher_helper.h:16
icp_customized.name
string name
Definition: icp_customized.py:45
icp.rotation
rotation
Definition: icp.py:74
Matcher
PM::Matcher Matcher
Definition: pypoint_matcher_helper.h:24
PointMatcher< float >
align_sequence.icp
icp
Definition: align_sequence.py:40
PointMatcherSupport::Logger
The logger interface, used to output warnings and informations.
Definition: PointMatcher.h:104
PointMatcher::DataPoints
A point cloud.
Definition: PointMatcher.h:207
testing::internal::string
::std::string string
Definition: gtest.h:1979
parseRotation
PM::TransformationParameters parseRotation(string &rotation, const int cloudDimension)
Definition: examples/icp.cpp:343
icp.translation
translation
Definition: icp.py:73
PointMatcher::Transformation
A function that transforms points and their descriptors given a transformation matrix.
Definition: PointMatcher.h:404
validateArgs
int validateArgs(const int argc, const char *argv[], bool &isVerbose, bool &isTransfoSaved, string &configFile, string &outputBaseFile, string &initTranslation, string &initRotation)
Definition: examples/icp.cpp:231
TransformationChecker
PM::TransformationChecker TransformationChecker
Definition: pypoint_matcher_helper.h:29
parseTranslation
PM::TransformationParameters parseTranslation(string &translation, const int cloudDimension)
Definition: examples/icp.cpp:306
Parameters
PM::Parameters Parameters
Definition: examples/icp.cpp:48
main
int main(int argc, const char *argv[])
Definition: examples/icp.cpp:72
icp.data
data
Definition: icp.py:50
icp.data_out
data_out
Definition: icp.py:93
ICP
PM::ICP ICP
Definition: pypoint_matcher_helper.h:33
PointMatcherSupport::CurrentBibliography::dump
void dump(std::ostream &os) const
Definition: Bibliography.cpp:241
icp.ref
ref
Definition: icp.py:49
Inspector
PM::Inspector Inspector
Definition: pypoint_matcher_helper.h:31
std
CurrentBibliography
PointMatcherSupport::CurrentBibliography CurrentBibliography
Definition: examples/icp.cpp:49
icp
Definition: icp.py:1
DUMP_REGISTRAR_CONTENT
#define DUMP_REGISTRAR_CONTENT(pm, name, bib)
Definition: examples/icp.cpp:210
PointMatcher< float >::get
static const PointMatcher & get()
Return instances.
Definition: Registry.cpp:147
PointMatcher::ErrorMinimizer
An error minimizer will compute a transformation matrix such as to minimize the error between the rea...
Definition: PointMatcher.h:531
PointMatcher::DataPoints::load
static DataPoints load(const std::string &fileName)
Load a point cloud from a file, determine format from extension.
Definition: pointmatcher/IO.cpp:353
PointMatcher.h
public interface
usage
void usage(const char *argv[])
Definition: examples/icp.cpp:381
OutlierFilter
PM::OutlierFilter OutlierFilter
Definition: pypoint_matcher_helper.h:25
dumpRegistrar
void dumpRegistrar(const PM &pm, const R &registrar, const std::string &name, CurrentBibliography &bib)
Definition: examples/icp.cpp:197
Bibliography.h
PointMatcher::TransformationParameters
Matrix TransformationParameters
A matrix holding the parameters a transformation.
Definition: PointMatcher.h:182
PointMatcherSupport::CurrentBibliography
Definition: Bibliography.h:51


libpointmatcher
Author(s):
autogenerated on Sun Dec 22 2024 03:21:53