MIRA
EigenFormat.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2012 by
3  * MetraLabs GmbH (MLAB), GERMANY
4  * and
5  * Neuroinformatics and Cognitive Robotics Labs (NICR) at TU Ilmenau, GERMANY
6  * All rights reserved.
7  *
8  * Contact: info@mira-project.org
9  *
10  * Commercial Usage:
11  * Licensees holding valid commercial licenses may use this file in
12  * accordance with the commercial license agreement provided with the
13  * software or, alternatively, in accordance with the terms contained in
14  * a written agreement between you and MLAB or NICR.
15  *
16  * GNU General Public License Usage:
17  * Alternatively, this file may be used under the terms of the GNU
18  * General Public License version 3.0 as published by the Free Software
19  * Foundation and appearing in the file LICENSE.GPL3 included in the
20  * packaging of this file. Please review the following information to
21  * ensure the GNU General Public License version 3.0 requirements will be
22  * met: http://www.gnu.org/copyleft/gpl.html.
23  * Alternatively you may (at your option) use any later version of the GNU
24  * General Public License if such license has been publicly approved by
25  * MLAB and NICR (or its successors, if any).
26  *
27  * IN NO EVENT SHALL "MLAB" OR "NICR" BE LIABLE TO ANY PARTY FOR DIRECT,
28  * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF
29  * THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF "MLAB" OR
30  * "NICR" HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * "MLAB" AND "NICR" SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
33  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
34  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
35  * ON AN "AS IS" BASIS, AND "MLAB" AND "NICR" HAVE NO OBLIGATION TO
36  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS.
37  */
38 
47 #ifndef _MIRA_EIGENFORMAT_H_
48 #define _MIRA_EIGENFORMAT_H_
49 
50 #ifndef Q_MOC_RUN
51 #include <boost/algorithm/string/trim.hpp>
52 #endif
53 
54 #include <platform/Platform.h>
55 
56 #include <Eigen/Eigen>
57 #include <Eigen/StdVector>
58 
59 #include <stream/NumericalStream.h>
60 #include <error/Exceptions.h>
61 
62 namespace mira {
63 
65 
70 {
71 public:
73 
74 public:
75  // some predefined formats.
77  return Eigen::IOFormat(precision, 0, ", ", ";\n", "", "", "[", "]");
78  }
80  return Eigen::IOFormat(precision, 0, ", ", "\n", "[", "]");
81  }
82  static Eigen::IOFormat eigen(int precision=-1) {
83  return Eigen::IOFormat();
84  }
86  return Eigen::IOFormat(precision, 0, ", ", ",\n", "[", "]", "[", "]");
87  }
88 
89 protected:
91 };
92 
94 
96 
97 // general implementation for any Eigen::MatrixBase<Derived>
98 template <typename MatrixType>
99 struct EigenFormatSizeHelper
100 {
101  static bool checkOrResize(MatrixType& matrix, int rows, int cols)
102  {
103  // in generic case just check if the size matches
104  return (matrix.rows() == rows && matrix.cols() == cols);
105  }
106 };
107 
108 // specialization for Eigen::Matrix that resizes the matrix if it is dynamic
109 template<typename Scalar, int Rows, int Cols, int Options, int MaxRows, int MaxCols>
110 struct EigenFormatSizeHelper<Eigen::Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>>
111 {
113  typedef Eigen::MatrixBase<Derived> MatrixType;
114  static bool checkOrResize(MatrixType& matrix, int rows, int cols)
115  {
116  Derived& m = static_cast<Derived&>(matrix);
117  if (Derived::RowsAtCompileTime == Eigen::Dynamic ||
118  Derived::ColsAtCompileTime == Eigen::Dynamic )
119  m.resize(rows, cols);
120  return (matrix.rows() == rows && matrix.cols() == cols);
121  }
122 };
123 
125 
127 
133 template <typename Derived>
135 {
136 private:
137 
138 public:
140  EigenFormat(format), mMatrix(matrix) {}
141 
148  friend std::ostream& operator<< (std::ostream & s,
150  {
151  format.print(s);
152  return s;
153  }
154 
156  friend std::istream& operator>> (std::istream & is,
158  {
159  static_assert(sizeof(Derived)==0, "operator>> is not supported for read-only matrices");
160  return is;
161  }
162 
163 private:
164 
165  void print(std::ostream& os) const
166  {
167  if (mMatrix.size() == 0) {
169  return;
170  }
171 
172  const typename Derived::Nested m = mMatrix;
173  typedef typename Derived::Scalar Scalar;
174  typedef typename Derived::Index Index;
175 
176  Index width = 0;
177 
178  std::streamsize precision;
179  if (mFormat.precision == Eigen::StreamPrecision)
180  precision = 0;
181  else if (mFormat.precision == Eigen::FullPrecision)
182  precision = 10; // TODO: obtain full precision
183  else
185 
186  bool alignCols = !(mFormat.flags & Eigen::DontAlignCols);
187  if (alignCols) {
188  // compute the largest width
189  for (Index j = 1; j < m.cols(); ++j)
190  for (Index i = 0; i < m.rows(); ++i) {
191  std::stringstream sstr;
192  NumericalOstream nos(sstr);
193  if(precision)
194  sstr.precision(precision);
195  nos << m.coeff(i, j);
196  width = std::max<Index>(width, Index(sstr.str().length()));
197  }
198  }
199 
200  NumericalOstream nos(os);
201  std::streamsize oldprecision = 0;
202  if(precision)
203  oldprecision = os.precision(precision);
204  os << mFormat.matPrefix;
205  for(Index i = 0; i < m.rows(); ++i) {
206  if(i)
207  os << mFormat.rowSpacer;
208  os << mFormat.rowPrefix;
209  if(width)
210  os.width(width);
211  nos << m.coeff(i, 0);
212  for(Index j = 1; j < m.cols(); ++j) {
213  os << mFormat.coeffSeparator;
214  if(width)
215  os.width(width);
216  nos << m.coeff(i, j);
217  }
218  os << mFormat.rowSuffix;
219  if(i < m.rows() - 1)
220  os << mFormat.rowSeparator;
221  }
222  os << mFormat.matSuffix;
223  if(precision)
224  os.precision(oldprecision);
225 
226  return;
227  }
228 
229 
230 protected:
233 };
234 
235 
241 template <typename Derived>
242 class TEigenFormat : public TEigenFormatReadOnly<Derived>
243 {
245 public:
247  Base(matrix, format) {}
248 
255  friend std::istream& operator>> (std::istream & is,
257  {
258  format.parse(is);
259  return is;
260  }
261 
262 private:
263 
264  void parse(std::istream& is)
265  {
266  typedef typename Eigen::internal::traits< Derived >::Scalar Scalar;
267 
268  NumericalIstream nis(is);
269 
270  Scalar val;
271  std::vector<Scalar> values;
272 
273  std::string matPrefix = boost::trim_copy(this->mFormat.matPrefix);
274  std::string matSuffix = boost::trim_copy(this->mFormat.matSuffix);
275  std::string rowPrefix = boost::trim_copy(this->mFormat.rowPrefix);
276  std::string rowSuffix = boost::trim_copy(this->mFormat.rowSuffix);
277  std::string rowSeparator = boost::trim_copy(this->mFormat.rowSeparator);
278  std::string coeffSeparator = boost::trim_copy(this->mFormat.coeffSeparator);
279 
280  // if we have no rowSuffix, then keep the new line as row separator
281  // if there is one (we need it to detect new rows)
282  if(rowSuffix.empty() && this->mFormat.rowSeparator=="\n")
283  rowSeparator = "\n";
284 
285  // if both rowSuffix and coeffSeparator are empty (and therefore
286  // equal, we have a problem, so try to swap the rowSuffix and the
287  // rowSeparator (this is valid for parsing and might solve the
288  // problem)
289  if(rowSuffix.empty() && coeffSeparator.empty()) {
290  std::swap(rowSuffix, rowSeparator);
291  }
292 
293  assert(rowSuffix != coeffSeparator);
294 
295  int rows = 0;
296  int cols = 0;
297  int col=0;
298 
299  // we must start with the prefix of the matrix
300  match(is, matPrefix);
301 
302  // start parsing the matrix now
303  while(true)
304  {
305  if(col==0) {
306  // next could be the rowPrefix, or the suffix of the matrix
307  // or we could reach eof. In the latter cases we have to break
308  if(match(is, rowPrefix, matSuffix)==2 || is.eof())
309  break;
310  }
311 
312  // read in next value from our numerical stream
313  nis >> val;
314  if(!is.fail()) {
315  ++col;
316  values.push_back(val);
317  } else
318  is.clear(); // if parsing of coeff failed, ignore that error and continue
319  // will detect syntax error afterwards if any
320 
321  // next one will be coeff separator or row suffix
322  if(match(is, coeffSeparator, rowSuffix)==2 || is.eof()) {
323 
324  // found end of row
325 
326  if(rows==0) {
327  cols = col; // save the number of columns if this was the first row ...
328  } else {
329  // ... otherwise, make sure that the number of columns is the same as in the first row
330  if(col!=cols)
331  MIRA_THROW(XIO, "Invalid number of columns, expected "
332  << cols << " but got " << col);
333  }
334  ++rows;
335  col = 0; // start new row
336 
337  // next could be the rowSeparator or the suffix of the matrix
338  if(match(is, rowSeparator, matSuffix)==2 || is.eof())
339  {
340  // found suffix of matrix, so we are done
341  break;
342  }
343 
344  // otherwise we expect another row:
345  //match(is, rowPrefix);
346 
347  } else {
348  // we found a coeffSeparator so continue with parsing the
349  // row in the next iteration
350  }
351  }
352  // phew, we finished parsing the matrix successfully, now
353  // resize the matrix to the found size (if it is a dynamic one) or
354  // throw an exception that the sizes do not match.
355 
356  if(!EigenFormatSizeHelper<Derived>::checkOrResize(this->mMatrix,rows,cols))
357  MIRA_THROW(XIO, "The size of the given matix ("
358  << this->mMatrix.rows() << "x" << this->mMatrix.cols() << ") "
359  "does not match the size of the read matrix ("
360  << rows << "x" << cols << ")");
361 
362  // now put the values into the matrix
363  std::size_t idx = 0;
364  for(int i=0; i<rows; ++i)
365  for(int j=0; j<cols; ++j, ++idx)
366  this->mMatrix(i,j) = values[idx];
367  }
368 
369 public:
370  // returns the next token from the stream
371  // - reads at most maxlength characters
372  // - keeps the keepwhitespace character (other whitespaces are skipped)
373  static std::string getToken(std::istream& is, std::size_t maxlength=255,
374  char keepwhitespace=0)
375  {
376  assert(maxlength>0);
377 
378  std::string token;
379 
380  // skip white spaces except 'keepwhitespace'
381  while(!is.eof())
382  {
383  char ch = is.peek();
384  if(!isspace(ch) || ch==keepwhitespace)
385  break;
386 
387  ch = is.get(); // read white space
388  }
389 
390  if(is.eof())
391  return token;
392 
393  // add the next char to the token
394  token.push_back(is.get());
395 
396  // read token until maxlength, eof or a whitespace is reached
397  for(std::size_t i=1; i<maxlength && !is.eof() && !isspace(is.peek()); ++i)
398  token.push_back(is.get()); // read chars of token
399 
400  return token;
401  }
402 
403  static void putback(std::istream& is, int num)
404  {
405  for(int i=0; i<num; ++i)
406  is.unget();
407  }
408 
409  // tries to match str1 OR str2 in the stream, throws an exception if no match
410  // returns 1 if str1 matches and 2 if str2 matches
411  static int match(std::istream& is, const std::string str1, const std::string str2)
412  {
413  std::size_t len = std::max(str1.size(), str2.size());
414 
415  if(len==0)
416  return 1; // if both strings are empty, then return immediately
417 
418  // check if first of second match string is a whitespace
419 
420  char whitespace1=0;
421  if(str1.size()==1 && isspace(str1[0]))
422  whitespace1=str1[0];
423 
424  char whitespace2=0;
425  if(str2.size()==1 && isspace(str2[0]))
426  whitespace2=str2[0];
427 
428  // we can not handle the case where both strings are white spaces
429  assert(whitespace1==0 || whitespace2==0);
430 
431  // checkout if we should match a whitespace
432  char whitespace = 0;
433  if(whitespace1!=0)
434  whitespace=whitespace1;
435  else if(whitespace2!=0)
436  whitespace=whitespace2;
437 
438  std::string token = getToken(is, len, whitespace);
439 
440  bool matches1st = (token.substr(0,str1.size()) == str1);
441  bool matches2nd = (token.substr(0,str2.size()) == str2);
442 
443  // if both match, return longest match
444  if(matches1st && matches2nd) {
445  if(str1.size()>=str2.size())
446  return 1;
447  else
448  return 2;
449  }
450 
451  if(matches1st && !matches2nd) {
452  // if the matching string is shorter than the amount of characters
453  // we have read, then we must put back those characters
454  if(str1.size() < token.size())
455  putback(is, token.size()-str1.size());
456  return 1;
457  }
458 
459  if(matches2nd && !matches1st) {
460  // put back the characters if we have read too much
461  if(str2.size() < token.size())
462  putback(is, token.size()-str2.size());
463  return 2;
464  }
465 
466  // no match at all
467  MIRA_THROW(XIO, "Expected either '" << str1 << "' or '" << str2
468  << "' in the input stream");
469  return 0;
470  }
471 
472  // tries to match str in the stream, throws an exception if no match
473  static void match(std::istream& is, const std::string str)
474  {
475  if(str.empty())
476  return;
477 
478  char whitespace=0;
479  if(str.size()==1 && isspace(str[0]))
480  whitespace=str[0];
481 
482  std::string token = getToken(is, str.size(), whitespace);
483 
484  if(token==str)
485  return;
486 
487  // no match
488  MIRA_THROW(XIO, "Expected '" << str << "' in the input stream");
489  }
490 
491 };
492 
521 template <typename Derived>
524 {
525  return TEigenFormat<Derived>(matrix, format);
526 }
527 
531 template <typename Derived>
534 {
536 }
537 
539 
540 } // namespace
541 
542 #endif
std::string matPrefix
std::string matSuffix
TEigenFormat< Derived > format(Eigen::MatrixBase< Derived > &matrix, Eigen::IOFormat format=EigenFormat::matlab())
Function for formatting an Eigen matrix using a special format.
Definition: EigenFormat.h:522
specialize cv::DataType for our ImgPixel and inherit from cv::DataType<Vec>
Definition: IOService.h:67
Definition: YawPitchRoll.h:684
Eigen::MatrixBase< Derived > & mMatrix
The matrix that gets formatted.
Definition: EigenFormat.h:232
static Eigen::IOFormat clean(int precision=4)
Definition: EigenFormat.h:79
friend std::istream & operator>>(std::istream &is, TEigenFormatReadOnly< Derived > format)
not supported for read-only matrices
Definition: EigenFormat.h:156
std::string rowSuffix
static Eigen::IOFormat eigen(int precision=-1)
Definition: EigenFormat.h:82
#define MIRA_THROW(ex, msg)
Macro for throwing an exception.
Definition: Exception.h:81
Template class wrapping the serializing functionality of eigen to support stream operators using a gi...
Definition: EigenFormat.h:242
EigenFormat(Eigen::IOFormat format)
Definition: EigenFormat.h:72
friend std::ostream & operator<<(std::ostream &s, const TEigenFormatReadOnly< Derived > &format)
Output stream operator for writing a matrix to stream in a given format.
Definition: EigenFormat.h:148
static void putback(std::istream &is, int num)
Definition: EigenFormat.h:403
Commonly used exception classes.
static void match(std::istream &is, const std::string str)
Definition: EigenFormat.h:473
TEigenFormat(Eigen::MatrixBase< Derived > &matrix, Eigen::IOFormat format)
Definition: EigenFormat.h:246
Numerical stream adapter that can be assigned to any input stream and allows streaming of numerical v...
Definition: NumericalStream.h:186
std::string rowSpacer
std::string rowPrefix
Eigen::IOFormat mFormat
Definition: EigenFormat.h:90
Template class wrapping the serializing functionality of eigen to support stream operators using a gi...
Definition: EigenFormat.h:134
friend std::istream & operator>>(std::istream &is, TEigenFormat< Derived > format)
Input stream operator for reading a matrix from stream in a given format.
Definition: EigenFormat.h:255
static Eigen::IOFormat matlab(int precision=4)
Definition: EigenFormat.h:76
static std::string getToken(std::istream &is, std::size_t maxlength=255, char keepwhitespace=0)
Definition: EigenFormat.h:373
std::string rowSeparator
PropertyHint precision(int p)
Sets the attribute "precision".
Definition: PropertyHint.h:285
static Eigen::IOFormat python(int precision=4)
Definition: EigenFormat.h:85
TEigenFormatReadOnly(Eigen::MatrixBase< Derived > &matrix, Eigen::IOFormat format)
Definition: EigenFormat.h:139
Base class for formatting eigen matrices.
Definition: EigenFormat.h:69
std::string coeffSeparator
Platform dependent defines and macros.
static int match(std::istream &is, const std::string str1, const std::string str2)
Definition: EigenFormat.h:411