MIRA
RasterTransformation.h
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 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_RASTERTRANSFORMATION_H_
48 #define _MIRA_RASTERTRANSFORMATION_H_
49 
50 #include <math/Truncate.h>
51 #include <geometry/Rect.h>
52 #include <geometry/Bresenham.h>
53 #include <transform/Pose.h>
54 
55 namespace mira {
56 
58 
133 {
134 
135 public:
137  RasterTransformation(const Rect2i & srcArea = Rect2i(0,0,1,1),
138  const Rect2i & tgtArea = Rect2i(0,0,1,1),
139  const Point2d & srcRef = Point2d(0.0, 0.0),
140  const Point2d & tgtRef = Point2d(0.0, 0.0),
141  double scale = 1.0,
142  double rotate = 0.0,
143  bool dense = false);
144 public:
145  class iterator
146  {
147  // The basic idea is to go through the target raster line by line and
148  // determine the corresponding line in the source raster. Line sampling
149  // distance is the smaller of the 2 raster cell sizes, so we do not miss
150  // cells in either source or target.
151  // For each line the exact real-valued start and end points in source
152  // and target raster are calculated (with respect to the transformation
153  // parameters). Points on the line are then defined by 3 changing plus 1
154  // constant coordinates (target x, source x, source y = changing,
155  // target y = fixed. Note that source y may be fixed, but only for
156  // particular rotation.)
157  // Line start/end points are clipped to the area borders so we do not
158  // access outside the defined area.
159  // Fast iteration over the line is done using a Bresenham algorithm.
160 
161  friend class RasterTransformation;
162 
163  public:
165  iterator();
166 
168  iterator(const iterator& other);
169 
170  public:
171 
173  iterator& operator=(const iterator& other);
174 
176  bool operator==(const iterator& other) const;
177 
179  bool operator!=(const iterator& other) const;
180 
182  const iterator& operator++();
183 
185  bool isValid() const {return mLineValid;};
186 
188  inline int srcX() const { return mBresenham.axis(0).current; }
189 
191  inline int srcY() const { return mBresenham.axis(1).current; }
192 
194  inline int tgtX() const { return mBresenham.axis(2).current; }
195 
197  inline int tgtY() const { return mTY; }
198 
199  protected:
200  bool clipLine(const Rect2i & area, Point3d & p1, Point3d & p2);
201 
202  bool initLine();
203  void setToBegin();
204  void setToEnd();
205 
206  public:
207 
209 
211 
212  double mCurrentLine;
213  int mTY;
215  };
216 
217 public:
218 
220  bool operator==(const RasterTransformation& other) const;
221 
223  bool operator!=(const RasterTransformation& other) const;
224 
225 public:
226 
228  const iterator& begin() const {return mBegin;}
229 
230 protected:
231 
235 
236  double mScale;
237  double mRotate;
238 
239  bool mDense;
240 
242 
245 
246  double mSinRot, mCosRot; // sin/cos of rotation angle
247  double mDxStart, mDxEnd; // target x of line start/end point
248  double mLineStep; // distance between lines in target (tgt y)
249  Point3d mRasterStep; // vector between 2 subsequent sample raster
250  // points on one 3d line (src x, src y, tgt x)
251 };
252 
254 // implementation section: RasterTransformation class implementation
256 
258 RasterTransformation(const Rect2i & srcArea,
259  const Rect2i & tgtArea,
260  const Point2d & srcRef,
261  const Point2d & tgtRef,
262  double scale,
263  double rotate,
264  bool dense)
265  : mSrcArea(srcArea.x0(), srcArea.y0(), srcArea.width()-1, srcArea.height()-1),
266  mTgtArea(tgtArea.x0(), tgtArea.y0(), tgtArea.width()-1, tgtArea.height()-1),
267  mSrcRef(srcRef),
268  mTgtRef(tgtRef),
269  mScale(scale),
270  mRotate(rotate),
271  mDense(dense)
272 {
273  if ((srcArea.width() < 1) || (srcArea.height() < 1) || (tgtArea.width() < 1) || (tgtArea.height() < 1))
274  return;
275 
276  mLineStep = std::min(1., std::abs(mScale));
277  if (mDense)
278  mLineStep /= sqrt(2.);
279 
280  // the truncate adds tiny inaccuracy in the general case, but ensures
281  // we meet expectations in special cases where sin/cos should be 0/1
282  // (which otherwise we don't, due to numerical precision limits)
283  mSinRot = mira::truncate(std::sin(mRotate), 15);
284  mCosRot = mira::truncate(std::cos(mRotate), 15);
285 
286  mDxStart = mTgtArea.x0() - mTgtRef.x();
287  mDxEnd = mTgtArea.x1() - mTgtRef.x();
288 
290  mSinRot * (mDxEnd - mDxStart) / mScale,
291  mDxEnd - mDxStart);
292 
293  double length = std::max(std::hypot(mRasterStep.x(), mRasterStep.y()),
294  std::abs(mRasterStep.z()));
295  mRasterStep /= length;
296 
297  mBegin.mT = this;
298 
299  if ((mTgtArea.width() == 0) || (mTgtArea.height() == 0) || (mScale == 0))
300  return;
301 
302  mBegin.setToBegin();
303 }
304 
305 inline bool RasterTransformation::
306 operator==(const RasterTransformation& other) const
307 {
308  return mSrcArea == other.mSrcArea &&
309  mTgtArea == other.mTgtArea &&
310  mSrcRef == other.mSrcRef &&
311  mTgtRef == other.mTgtRef &&
312  mScale == other.mScale &&
313  mRotate == other.mRotate &&
314  mDense == other.mDense;
315 }
316 
317 inline bool RasterTransformation::
318 operator!=(const RasterTransformation& other) const
319 {
320  return !(*this == other);
321 }
322 
324 
326  mT(NULL), mLineValid(false) {}
327 
330  : mT(other.mT),
331  mBresenham(other.mBresenham),
332  mCurrentLine(other.mCurrentLine),
333  mTY(other.mTY),
334  mLineValid(other.mLineValid)
335 {
336 }
337 
340 {
341  mT = other.mT;
342  mBresenham = other.mBresenham;
343  mCurrentLine = other.mCurrentLine;
344  mTY = other.mTY;
345  mLineValid = other.mLineValid;
346 
347  return *this;
348 }
349 
351  Point3d & p1, Point3d & p2)
352 {
353  // source: openCV
354  // adaptation: 2d -> 3d (precisely: 3d line clipped to 2d area),
355  // int -> double
356  // size -> area (i.e. left/top != 0)
357 
358  int left = area.x0(), right = area.x1(),
359  top = area.y0(), bottom = area.y1();
360 
361  int c1, c2;
362 
363  double & x1 = p1.x();
364  double & y1 = p1.y();
365  double & z1 = p1.z();
366  double & x2 = p2.x();
367  double & y2 = p2.y();
368  double & z2 = p2.z();
369 
370  c1 = (x1 < left) + ((x1 > right) << 1) + ((y1 < top) << 2) + ((y1 > bottom) << 3);
371  c2 = (x2 < left) + ((x2 > right) << 1) + ((y2 < top) << 2) + ((y2 > bottom) << 3);
372 
373  if( ((c1 & c2) == 0) && ((c1 | c2) != 0 )) // one or both points outside
374  // but not both to the same side!
375  {
376  double a;
377  if( c1 & 12 ) // y1 is outside
378  {
379  // move p1 to area top/bottom
380  a = c1 < 8 ? top : bottom;
381  x1 += (a - y1) * (x2 - x1) / (y2 - y1);
382  z1 += (a - y1) * (z2 - z1) / (y2 - y1);
383  y1 = a;
384  c1 = (x1 < left) + (x1 > right) * 2;
385  }
386  if( c2 & 12 ) // y2 is outside
387  {
388  // move p2 to area top/bottom
389  a = c2 < 8 ? top : bottom;
390  x2 += (a - y2) * (x2 - x1) / (y2 - y1);
391  z2 += (a - y2) * (z2 - z1) / (y2 - y1);
392  y2 = a;
393  c2 = (x2 < left) + (x2 > right) * 2;
394  }
395  if( ((c1 & c2) == 0) && ((c1 | c2) != 0) ) // one or both x outside
396  // but not both to the same side!
397  {
398  if( c1 ) // x1 is outside
399  {
400  // move p1 to area left/right
401  a = c1 == 1 ? left : right;
402  y1 += (a - x1) * (y2 - y1) / (x2 - x1);
403  z1 += (a - x1) * (z2 - z1) / (x2 - x1);
404  x1 = a;
405  c1 = 0;
406  }
407  if( c2 ) // x2 is outside
408  {
409  // move p2 to area left/right
410  a = c2 == 1 ? left : right;
411  y2 += (a - x2) * (y2 - y1) / (x2 - x1);
412  z2 += (a - x2) * (z2 - z1) / (x2 - x1);
413  x2 = a;
414  c2 = 0;
415  }
416  }
417  }
418 
419  return (c1 | c2) == 0;
420 }
421 
423 {
424  mTY = floor(mCurrentLine + 0.5 * mT->mLineStep);
425 
426  if (mTY > mT->mTgtArea.y1())
427  return (mLineValid = false);
428 
429  double dy = mCurrentLine + 0.5f * mT->mLineStep - mT->mTgtRef.y();
430 
431  Point3d start(mT->mSrcRef.x()
432  + (mT->mCosRot * mT->mDxStart - mT->mSinRot * dy) / mT->mScale,
433  mT->mSrcRef.y()
434  + (mT->mSinRot * mT->mDxStart + mT->mCosRot * dy) / mT->mScale,
435  mT->mTgtArea.x0());
436 
437  Point3d end(mT->mSrcRef.x()
438  + (mT->mCosRot * mT->mDxEnd - mT->mSinRot * dy) / mT->mScale,
439  mT->mSrcRef.y()
440  + (mT->mSinRot * mT->mDxEnd + mT->mCosRot * dy) / mT->mScale,
441  mT->mTgtArea.x1());
442 
443  Point3d startClipped = start;
444 
445  // clip start and end to make sure we don't access outside the designated src area
446  mLineValid = clipLine(mT->mSrcArea, startClipped, end);
447 
448  // convex area -> there can be no separate valid lines
449  // if we hit an empty line after begin, we are already at the end
450  if (!mLineValid)
451  return false;
452 
453  // go to the next raster point inside the clipped interval
454  // (advance to multiple of RasterStep)
455  if (startClipped != start)
456  {
457  double steps = 0;
458 
459  // normally these values are all the same
460  // but because of (extremely rare) numerical issues we rather play safe here, taking the max
461 
462  if (mT->mRasterStep.x() != 0)
463  steps = std::max(steps, std::ceil((startClipped.x() - start.x()) / mT->mRasterStep.x()));
464 
465  if (mT->mRasterStep.y() != 0)
466  steps = std::max(steps, std::ceil((startClipped.y() - start.y()) / mT->mRasterStep.y()));
467 
468  if (mT->mRasterStep.z() != 0)
469  steps = std::max(steps, std::ceil((startClipped.z() - start.z()) / mT->mRasterStep.z()));
470 
471  startClipped = start + steps * mT->mRasterStep;
472  }
473 
474  // try to avoid sample points near cell borders, as those are sensitive to
475  // small deviations (e.g. due to floating point resolution limits).
476  // unfortunately it is not possible to optimally solve this issue in general.
477  // it seems to help in some cases (and not hurt ever) to move the starting
478  // sample point away from the cell border if it is close.
479  if (std::abs(startClipped.z() - std::round(startClipped.z())) < 0.25)
480  {
481  startClipped += mT->mRasterStep/2;
482  }
483 
484  // check if we stepped beyond end
485  if (((startClipped.x() > start.x()) && (startClipped.x() > end.x())) ||
486  ((startClipped.x() < start.x()) && (startClipped.x() < end.x())) )
487  {
488  return (mLineValid = false);
489  }
490 
491  // additional 4th axis controls the step size
492  Point3d distance = end - startClipped;
493  double l = std::max(std::hypot(distance.x(), distance.y()), std::abs(distance.z()));
494 
495  Point<double, 4> startBresenham;
496  startBresenham[0] = startClipped.x();
497  startBresenham[1] = startClipped.y();
498  startBresenham[2] = startClipped.z();
499  startBresenham[3] = 0;
500 
501  Point<double,4> endBresenham;
502  endBresenham[0] = end.x();
503  endBresenham[1] = end.y();
504  endBresenham[2] = end.z();
505  if (mT->mDense)
506  endBresenham[3] = sqrt(2.)*l;
507  else
508  endBresenham[3] = l;
509 
510  mBresenham.init(startBresenham, endBresenham);
511 
512  return true;
513 }
514 
515 inline const RasterTransformation::iterator&
517 {
518  if (mBresenham.hasNext())
519  {
520  ++mBresenham;
521  return *this;
522  }
523 
524  mCurrentLine += mT->mLineStep;
525  initLine();
526 
527  return *this;
528 }
529 
531 {
532  mCurrentLine = mT->mTgtArea.y0();
533  while (!initLine())
534  {
535  if (mCurrentLine >= mT->mTgtArea.y1())
536  return;
537 
538  mCurrentLine += mT->mLineStep;
539  }
540 }
541 
543 
544 } // namespace
545 
546 #endif
Rect2d mSrcAreaF
Definition: RasterTransformation.h:233
double mRotate
Definition: RasterTransformation.h:237
double mDxEnd
Definition: RasterTransformation.h:247
int tgtY() const
Return current y coordinate in target raster.
Definition: RasterTransformation.h:197
Rect< int, 2 > Rect2i
A 2D rect with integer precision.
Definition: Rect.h:740
bool operator!=(const RasterTransformation &other) const
Comparison.
Definition: RasterTransformation.h:318
specialize cv::DataType for our ImgPixel and inherit from cv::DataType<Vec>
Definition: IOService.h:67
Point3d mRasterStep
Definition: RasterTransformation.h:249
const Axis & axis(uint32 d) const
Returns axis structure for a specified dimension.
Definition: Bresenham.h:424
Point< double, 2 > Point2d
a 2D 64 bit floating precision point
Definition: Point.h:229
Point2d mTgtRef
Definition: RasterTransformation.h:234
const iterator & operator++()
Advances to the next coordinate pair.
Definition: RasterTransformation.h:516
double mLineStep
Definition: RasterTransformation.h:248
bool initLine()
Definition: RasterTransformation.h:422
T truncate(T value, uint32 decimals)
Truncates a floating point value to a given number of decimals.
Definition: Truncate.h:69
int mTY
Definition: RasterTransformation.h:213
Rect2i mTgtArea
Definition: RasterTransformation.h:232
int srcY() const
Return current y coordinate in source raster.
Definition: RasterTransformation.h:191
const iterator & begin() const
Iterator to begin of raster mapping (first coordinate pair)
Definition: RasterTransformation.h:228
Rect2i mSrcArea
Definition: RasterTransformation.h:232
GeneralBresenhamLineIterator< 4, double, 3, 10000 > mBresenham
Definition: RasterTransformation.h:210
bool operator!=(const iterator &other) const
Comparison.
iterator mBegin
Definition: RasterTransformation.h:241
double mCurrentLine
Definition: RasterTransformation.h:212
RasterTransformation * mT
Definition: RasterTransformation.h:208
double mScale
Definition: RasterTransformation.h:236
Provides multidimensional rectangle templates.
This header provides a set of functions to iterate over lines with the Bresenham or Bresenham Run-Sli...
Map a rectangular area from one raster into another, with an arbitrary transformation (scale...
Definition: RasterTransformation.h:132
bool clipLine(const Rect2i &area, Point3d &p1, Point3d &p2)
Definition: RasterTransformation.h:350
int srcX() const
Return current x coordinate in source raster.
Definition: RasterTransformation.h:188
void setToBegin()
Definition: RasterTransformation.h:530
double mSinRot
implementation details
Definition: RasterTransformation.h:246
double mDxStart
Definition: RasterTransformation.h:247
Definition: RasterTransformation.h:145
iterator & operator=(const iterator &other)
Assignment operator.
Definition: RasterTransformation.h:339
RasterTransformation(const Rect2i &srcArea=Rect2i(0, 0, 1, 1), const Rect2i &tgtArea=Rect2i(0, 0, 1, 1), const Point2d &srcRef=Point2d(0.0, 0.0), const Point2d &tgtRef=Point2d(0.0, 0.0), double scale=1.0, double rotate=0.0, bool dense=false)
Constructor with default values.
Definition: RasterTransformation.h:258
bool mLineValid
Definition: RasterTransformation.h:214
bool isValid() const
Valid iterator?
Definition: RasterTransformation.h:185
bool mDense
Definition: RasterTransformation.h:239
Typedefs for different Pose datatypes that are internally RigidTransforms.
Point2d mSrcRef
Definition: RasterTransformation.h:234
Point< double, 3 > Point3d
a 3D 64 bit floating precision point
Definition: Point.h:305
double mCosRot
Definition: RasterTransformation.h:246
int tgtX() const
Return current x coordinate in target raster.
Definition: RasterTransformation.h:194
Truncates a floating point value to a given number of decimals.
iterator()
Default constructor.
Definition: RasterTransformation.h:325
bool operator==(const RasterTransformation &other) const
Comparison.
Definition: RasterTransformation.h:306
bool operator==(const iterator &other) const
Comparison.