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