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() = default;
171 
173  iterator(const iterator& other) = default;
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;
220  bool mLineValid = false;
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 invalidArea() 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 (!srcArea.isValid() ||
288  !tgtArea.isValid() ||
289  (tgtArea.width() == 0) ||
290  (tgtArea.height() == 0) ||
291  (scale == 0));
292 }
293 
295 {
296  if (config.invalidArea())
297  return;
298 
299  lineStep = std::min(1., std::abs(config.scale));
300  if (config.dense)
301  lineStep /= sqrt(2.);
302 
303  // the truncate adds tiny inaccuracy in the general case, but ensures
304  // we meet expectations in special cases where sin/cos should be 0/1
305  // (which otherwise we don't, due to numerical precision limits)
306  sinRot = mira::truncate(std::sin(config.rotate), 15);
307  cosRot = mira::truncate(std::cos(config.rotate), 15);
308 
309  dxStart = config.tgtArea.x0() - config.tgtRef.x();
310  dxEnd = config.tgtArea.x1() - config.tgtRef.x();
311 
312  rasterStep = Point3d(cosRot * (dxEnd - dxStart) / config.scale,
313  sinRot * (dxEnd - dxStart) / config.scale,
314  dxEnd - dxStart);
315 
316  double length = std::max(std::hypot(rasterStep.x(), rasterStep.y()),
317  std::abs(rasterStep.z()));
318  rasterStep /= length;
319 }
320 
322  const Rect2i & tgtArea,
323  const Point2d & srcRef,
324  const Point2d & tgtRef,
325  double scale,
326  double rotate,
327  bool dense)
328  : mConfig{Rect2i(srcArea.x0(), srcArea.y0(), srcArea.width()-1, srcArea.height()-1),
329  Rect2i(tgtArea.x0(), tgtArea.y0(), tgtArea.width()-1, tgtArea.height()-1),
330  srcRef, tgtRef, scale, rotate, dense},
331  mImpl(mConfig),
332  mBegin(this)
333 {
334 }
335 
337  : mConfig(other.mConfig),
338  mImpl(other.mImpl),
339  mBegin(this)
340 {
341 }
342 
344 {
345  mConfig = other.mConfig;
346  mImpl = other.mImpl;
347  mBegin = iterator(this);
348 
349  return *this;
350 }
351 
353 {
354  return mConfig == other.mConfig;
355 }
356 
358 
360  : mT(rt)
361 {
362  if (rt->mConfig.invalidArea())
363  return;
364 
365  setToBegin();
366 }
367 
370 {
371  mT = other.mT;
372  mBresenham = other.mBresenham;
373  mCurrentLine = other.mCurrentLine;
374  mTY = other.mTY;
375  mLineValid = other.mLineValid;
376 
377  return *this;
378 }
379 
380 inline bool RasterTransformation::clipLine(const Rect2i & area,
381  Point3d & p1, Point3d & p2)
382 {
383  // source: openCV
384  // adaptation: 2d -> 3d (precisely: 3d line clipped to 2d area),
385  // int -> double
386  // size -> area (i.e. left/top != 0)
387 
388  int left = area.x0();
389  int right = area.x1();
390  int top = area.y0();
391  int bottom = area.y1();
392 
393  double & x1 = p1.x();
394  double & y1 = p1.y();
395  double & z1 = p1.z();
396  double & x2 = p2.x();
397  double & y2 = p2.y();
398  double & z2 = p2.z();
399 
400  int c1 = (x1 < left) + ((x1 > right) << 1) + ((y1 < top) << 2) + ((y1 > bottom) << 3);
401  int c2 = (x2 < left) + ((x2 > right) << 1) + ((y2 < top) << 2) + ((y2 > bottom) << 3);
402 
403  if( ((c1 & c2) == 0) && ((c1 | c2) != 0 )) // one or both points outside
404  // but not both to the same side!
405  {
406  double a;
407  if( c1 & 12 ) // y1 is outside
408  {
409  // move p1 to area top/bottom
410  a = c1 < 8 ? top : bottom;
411  x1 += (a - y1) * (x2 - x1) / (y2 - y1);
412  z1 += (a - y1) * (z2 - z1) / (y2 - y1);
413  y1 = a;
414  c1 = (x1 < left) + (x1 > right) * 2;
415  }
416  if( c2 & 12 ) // y2 is outside
417  {
418  // move p2 to area top/bottom
419  a = c2 < 8 ? top : bottom;
420  x2 += (a - y2) * (x2 - x1) / (y2 - y1);
421  z2 += (a - y2) * (z2 - z1) / (y2 - y1);
422  y2 = a;
423  c2 = (x2 < left) + (x2 > right) * 2;
424  }
425  if( ((c1 & c2) == 0) && ((c1 | c2) != 0) ) // one or both x outside
426  // but not both to the same side!
427  {
428  if( c1 ) // x1 is outside
429  {
430  // move p1 to area left/right
431  a = c1 == 1 ? left : right;
432  y1 += (a - x1) * (y2 - y1) / (x2 - x1);
433  z1 += (a - x1) * (z2 - z1) / (x2 - x1);
434  x1 = a;
435  c1 = 0;
436  }
437  if( c2 ) // x2 is outside
438  {
439  // move p2 to area left/right
440  a = c2 == 1 ? left : right;
441  y2 += (a - x2) * (y2 - y1) / (x2 - x1);
442  z2 += (a - x2) * (z2 - z1) / (x2 - x1);
443  x2 = a;
444  c2 = 0;
445  }
446  }
447  }
448 
449  return (c1 | c2) == 0;
450 }
451 
453 {
454  mTY = floor(mCurrentLine + 0.5 * mT->mImpl.lineStep);
455 
456  if (mTY > mT->mConfig.tgtArea.y1())
457  return (mLineValid = false);
458 
459  double dy = mCurrentLine + 0.5f * mT->mImpl.lineStep - mT->mConfig.tgtRef.y();
460 
461  Point3d start(mT->mConfig.srcRef.x()
462  + (mT->mImpl.cosRot * mT->mImpl.dxStart - mT->mImpl.sinRot * dy) / mT->mConfig.scale,
463  mT->mConfig.srcRef.y()
464  + (mT->mImpl.sinRot * mT->mImpl.dxStart + mT->mImpl.cosRot * dy) / mT->mConfig.scale,
465  mT->mConfig.tgtArea.x0());
466 
467  Point3d end(mT->mConfig.srcRef.x()
468  + (mT->mImpl.cosRot * mT->mImpl.dxEnd - mT->mImpl.sinRot * dy) / mT->mConfig.scale,
469  mT->mConfig.srcRef.y()
470  + (mT->mImpl.sinRot * mT->mImpl.dxEnd + mT->mImpl.cosRot * dy) / mT->mConfig.scale,
471  mT->mConfig.tgtArea.x1());
472 
473  Point3d startClipped = start;
474 
475  // clip start and end to make sure we don't access outside the designated src area
476  mLineValid = clipLine(mT->mConfig.srcArea, startClipped, end);
477 
478  // convex area -> there can be no separate valid lines
479  // if we hit an empty line after begin, we are already at the end
480  if (!mLineValid)
481  return false;
482 
483  // go to the next raster point inside the clipped interval
484  // (advance to multiple of RasterStep)
485  if (startClipped != start)
486  {
487  double steps = 0;
488 
489  // normally these values are all the same
490  // but because of (extremely rare) numerical issues we rather play safe here, taking the max
491 
492  if (mT->mImpl.rasterStep.x() != 0)
493  steps = std::max(steps, std::ceil((startClipped.x() - start.x()) / mT->mImpl.rasterStep.x()));
494 
495  if (mT->mImpl.rasterStep.y() != 0)
496  steps = std::max(steps, std::ceil((startClipped.y() - start.y()) / mT->mImpl.rasterStep.y()));
497 
498  if (mT->mImpl.rasterStep.z() != 0)
499  steps = std::max(steps, std::ceil((startClipped.z() - start.z()) / mT->mImpl.rasterStep.z()));
500 
501  startClipped = start + steps * mT->mImpl.rasterStep;
502  }
503 
504  // try to avoid sample points near cell borders, as those are sensitive to
505  // small deviations (e.g. due to floating point resolution limits).
506  // unfortunately it is not possible to optimally solve this issue in general.
507  // it seems to help in some cases (and not hurt ever) to move the starting
508  // sample point away from the cell border if it is close.
509  if (std::abs(startClipped.z() - std::round(startClipped.z())) < 0.25)
510  {
511  startClipped += mT->mImpl.rasterStep/2;
512  }
513 
514  // check if we stepped beyond end
515  if (((startClipped.x() > start.x()) && (startClipped.x() > end.x())) ||
516  ((startClipped.x() < start.x()) && (startClipped.x() < end.x())) )
517  {
518  return (mLineValid = false);
519  }
520 
521  // additional 4th axis controls the step size
522  Point3d distance = end - startClipped;
523  double l = std::max(std::hypot(distance.x(), distance.y()), std::abs(distance.z()));
524 
525  Point<double, 4> startBresenham;
526  startBresenham[0] = startClipped.x();
527  startBresenham[1] = startClipped.y();
528  startBresenham[2] = startClipped.z();
529  startBresenham[3] = 0;
530 
531  Point<double,4> endBresenham;
532  endBresenham[0] = end.x();
533  endBresenham[1] = end.y();
534  endBresenham[2] = end.z();
535  if (mT->mConfig.dense)
536  endBresenham[3] = sqrt(2.)*l;
537  else
538  endBresenham[3] = l;
539 
540  mBresenham.init(startBresenham, endBresenham);
541 
542  return true;
543 }
544 
545 inline const RasterTransformation::iterator&
547 {
548  if (mBresenham.hasNext())
549  {
550  ++mBresenham;
551  return *this;
552  }
553 
554  mCurrentLine += mT->mImpl.lineStep;
555  initLine();
556 
557  return *this;
558 }
559 
561 {
562  mCurrentLine = mT->mConfig.tgtArea.y0();
563  while (!initLine())
564  {
565  if (mCurrentLine >= mT->mConfig.tgtArea.y1())
566  return;
567 
568  mCurrentLine += mT->mImpl.lineStep;
569  }
570 }
571 
573 
574 } // namespace
575 
576 #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:742
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:231
const iterator & operator++()
Advances to the next coordinate pair.
Definition: RasterTransformation.h:546
bool dense
Definition: RasterTransformation.h:242
bool initLine()
Definition: RasterTransformation.h:452
RasterTransformation & operator=(const RasterTransformation &other)
Definition: RasterTransformation.h:343
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:380
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.
iterator()=default
Default constructor.
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:560
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:369
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:321
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:294
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:318
int tgtX() const
Return current x coordinate in target raster.
Definition: RasterTransformation.h:202
Definition: RasterTransformation.h:235
Truncates a floating point value to a given number of decimals.
bool operator==(const RasterTransformation &other) const
Comparison.
Definition: RasterTransformation.h:352
bool invalidArea() const
Definition: RasterTransformation.h:286
bool operator==(const iterator &other) const
Comparison.