[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

polygon.hxx VIGRA

1 /************************************************************************/
2 /* */
3 /* Copyright 1998-2010 by Ullrich Koethe */
4 /* */
5 /* This file is part of the VIGRA computer vision library. */
6 /* The VIGRA Website is */
7 /* http://hci.iwr.uni-heidelberg.de/vigra/ */
8 /* Please direct questions, bug reports, and contributions to */
9 /* ullrich.koethe@iwr.uni-heidelberg.de or */
10 /* vigra@informatik.uni-hamburg.de */
11 /* */
12 /* Permission is hereby granted, free of charge, to any person */
13 /* obtaining a copy of this software and associated documentation */
14 /* files (the "Software"), to deal in the Software without */
15 /* restriction, including without limitation the rights to use, */
16 /* copy, modify, merge, publish, distribute, sublicense, and/or */
17 /* sell copies of the Software, and to permit persons to whom the */
18 /* Software is furnished to do so, subject to the following */
19 /* conditions: */
20 /* */
21 /* The above copyright notice and this permission notice shall be */
22 /* included in all copies or substantial portions of the */
23 /* Software. */
24 /* */
25 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND */
26 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES */
27 /* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND */
28 /* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT */
29 /* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, */
30 /* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING */
31 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR */
32 /* OTHER DEALINGS IN THE SOFTWARE. */
33 /* */
34 /************************************************************************/
35 
36 #ifndef VIGRA_POLYGON_HXX
37 #define VIGRA_POLYGON_HXX
38 
39 #include <cmath>
40 #include <cstdlib>
41 #include <iterator>
42 #include <algorithm>
43 #include "config.hxx"
44 #include "error.hxx"
45 #include "tinyvector.hxx"
46 #include "array_vector.hxx"
47 #include "gaussians.hxx"
48 #include "splines.hxx"
49 #include "linear_solve.hxx"
50 
51 namespace vigra {
52 
53 namespace detail {
54 
55 template < class Point >
56 bool pointYXOrdering(Point const & p1, Point const & p2)
57 {
58  return (p1[1]<p2[1]) || (p1[1] == p2[1] && p1[0] < p2[0]);
59 }
60 
61 template < class Point >
62 bool orderedClockwise(const Point &O, const Point &A, const Point &B)
63 {
64  return (A[0] - O[0]) * (B[1] - O[1]) - (A[1] - O[1]) * (B[0] - O[0]) <= 0;
65 }
66 
67 } // namespace detail
68 
69 
70 /** \addtogroup Geometry
71 */
72 //@{
73 
74  /** Polygons in two and higher dimenions.
75 
76  */
77 template<class POINT=TinyVector<double, 2> >
78 class Polygon
79 : protected ArrayVector<POINT>
80 {
81  public:
82  typedef ArrayVector<POINT> Base;
83 
84  typedef POINT Point;
85  typedef typename Base::value_type value_type;
86  typedef typename Base::reference reference;
87  typedef typename Base::const_reference const_reference;
88  typedef typename Base::pointer pointer;
89  typedef typename Base::const_pointer const_pointer;
90  typedef typename Base::iterator iterator;
91  typedef typename Base::const_iterator const_iterator;
92  typedef typename Base::reverse_iterator reverse_iterator;
93  typedef typename Base::const_reverse_iterator const_reverse_iterator;
94  typedef typename Base::size_type size_type;
95  typedef typename Base::difference_type difference_type;
96  typedef typename POINT::value_type coordinate_type;
97 
98  using Base::size;
99  using Base::empty;
100  using Base::begin;
101  using Base::end;
102  using Base::cbegin;
103  using Base::cend;
104  using Base::rbegin;
105  using Base::rend;
106  using Base::crbegin;
107  using Base::crend;
108 
109  // use default copy constructor
110 
111  Polygon()
112  : length_(0.0),
113  lengthValid_(false),
114  partialArea_(0.0),
115  partialAreaValid_(false)
116  {}
117 
118  Polygon(size_type n)
119  : Base(n, Point()),
120  lengthValid_(false),
121  partialAreaValid_(false)
122  {}
123 
124  template <class InputIterator>
125  Polygon(InputIterator b, InputIterator e)
126  : Base(b, e),
127  lengthValid_(false),
128  partialAreaValid_(false)
129  {}
130 
131  void clear()
132  {
133  invalidateProperties();
134  Base::clear();
135  }
136 
137  void invalidateProperties()
138  {
139  lengthValid_ = false;
140  partialAreaValid_ = false;
141  }
142 
143  double length() const
144  {
145  if(!lengthValid_)
146  {
147  length_ = 0.0;
148  for(unsigned int i = 1; i < size(); ++i)
149  length_ += ((*this)[i] - (*this)[i-1]).magnitude();
150  lengthValid_ = true;
151  }
152  return length_;
153  }
154 
155  double partialArea() const
156  {
157  if(!partialAreaValid_)
158  {
159  partialArea_ = 0.0;
160  for(unsigned int i = 1; i < size(); ++i)
161  partialArea_ += ((*this)[i][0]*(*this)[i-1][1] -
162  (*this)[i][1]*(*this)[i-1][0]);
163  partialArea_ *= 0.5;
164  partialAreaValid_ = true;
165  }
166  return partialArea_;
167  }
168 
169  double area() const
170  {
171  vigra_precondition(closed(),
172  "Polygon::area() requires polygon to be closed!");
173  return abs(partialArea());
174  }
175 
176  /// Returns true iff the last and first points are equal or the polygon is empty.
177  bool closed() const
178  {
179  return size() <= 1 || back() == front();
180  }
181 
182  /** Linearly interpolate at <tt>offset</tt> between knots
183  <tt>index</tt> and <tt>index+1</tt>.
184 
185  Preconditions: <tt>0 <= index < size()-1</tt> and <tt>0 <= offset <= 1</tt>.
186  */
187  Point interpolate(unsigned int index, double offset) const
188  {
189  if(index < size() - 1)
190  return (1.0 - offset) * (*this)[index] + offset * (*this)[index+1];
191  else
192  return (*this)[index];
193  }
194 
195  /**
196  * Tests whether the given point lies within this polygon.
197  * Requires that this polygon is closed.
198 
199  * Points which lie directly on the polylines or coincide with a knot
200  * are considered inside (this behavior is consistent with fillPolygon()).
201  * Parameter \a tolerance (interpreted as an absolute error bound)
202  * controls the numerical accuracy of this test.
203  */
204  bool contains(const_reference point, coordinate_type tolerance) const;
205 
206  bool contains(const_reference point) const
207  {
208  return contains(point, 2.0*NumericTraits<coordinate_type>::epsilon());
209  }
210 
211  void push_back(const_reference v)
212  {
213  if(size())
214  {
215  if(lengthValid_)
216  length_ += (v - back()).magnitude();
217  if(partialAreaValid_)
218  partialArea_ += 0.5*(v[0]*back()[1] - v[1]*back()[0]);
219  }
220  push_back_unsafe(v);
221  }
222 
223  void push_back_unsafe(const_reference v)
224  {
225  Base::push_back(v);
226  }
227 
228  void extend(const Polygon &other)
229  {
230  if(!other.size())
231  return;
232 
233  const_iterator otherBegin(other.begin());
234  if(size())
235  {
236  if(*otherBegin == Base::back())
237  {
238  // don't copy first pixel
239  ++otherBegin;
240  }
241  else
242  {
243  if(lengthValid_)
244  length_ += (other.front() - Base::back()).magnitude();
245  if(partialAreaValid_)
246  partialArea_ += (other.front()[0]*Base::back()[1] -
247  other.front()[1]*Base::back()[0]);
248  }
249  }
250  if(lengthValid_)
251  length_ += other.length();
252  if(partialAreaValid_)
253  partialArea_ += other.partialArea();
254  Base::insert(Base::end(), otherBegin, other.end());
255  }
256 
257  void setPoint(unsigned int pos, const_reference x)
258  {
259  invalidateProperties();
260  Base::operator[](pos) = x;
261  }
262 
263  // doesn't call invalidateProperties()
264  void setPointUnsafe(unsigned int pos, const_reference x)
265  {
266  Base::operator[](pos) = x;
267  }
268 
269  // alternative, but it will also invalidate if the caller only reads
270  // reads the return value.
271  // reference operator[](unsigned int pos)
272  // {
273  // invalidateProperties();
274  // return Base::operator[](pos);
275  // }
276 
277  const_reference operator[](unsigned int pos) const
278  {
279  return Base::operator[](pos);
280  }
281 
282  const_reference front() const
283  {
284  return Base::front();
285  }
286 
287  const_reference back() const
288  {
289  return Base::back();
290  }
291 
292  iterator begin()
293  {
294  invalidateProperties();
295  return Base::begin();
296  }
297 
298  iterator end()
299  {
300  invalidateProperties();
301  return Base::end();
302  }
303 
304  reverse_iterator rbegin()
305  {
306  invalidateProperties();
307  return Base::rbegin();
308  }
309 
310  reverse_iterator rend()
311  {
312  invalidateProperties();
313  return Base::rend();
314  }
315 
316  void erase(iterator pos)
317  {
318  invalidateProperties();
319  Base::erase(pos);
320  }
321 
322  void erase(iterator pos, iterator end)
323  {
324  invalidateProperties();
325  Base::erase(pos, end);
326  }
327 
328  iterator insert(iterator pos, const_reference x)
329  {
330  invalidateProperties();
331  return Base::insert(pos, x);
332  }
333 
334  template <class InputIterator>
335  iterator insert(iterator pos, InputIterator i, InputIterator end)
336  {
337  invalidateProperties();
338  return Base::insert(pos, i, end);
339  }
340 
341  Polygon split(unsigned int pos)
342  {
343  Polygon result;
344  if(pos == 0)
345  {
346  swap(result);
347  }
348  else if(pos < size())
349  {
350  result.insert(result.begin(), begin() + pos, end());
351  erase(begin() + pos, end());
352  }
353  return result;
354  }
355 
356  template <class Sequence>
357  void arcLengthList(Sequence & arcLengths) const
358  {
359  double length = 0.0;
360  arcLengths.push_back(0.0);
361  for(unsigned int i = 1; i < size(); ++i)
362  {
363  length += ((*this)[i] - (*this)[i-1]).magnitude();
364  arcLengths.push_back(length);
365  }
366  }
367 
368  /** Find the point on the polygon that corresponds to the given quantile.
369 
370  \a quantile must be in <tt>[0.0, 1.0]</tt>. The result of this function
371  can be used as input to <tt>interpolate()</tt>. For example,
372  the following code computes the point in the middle of the polygon:
373 
374  \code
375  double c = poly.arcLengthQuantile(0.5);
376  Point center = poly.interpolate((int)floor(c), c - floor(c));
377  \endcode
378  */
379  double arcLengthQuantile(double quantile) const
380  {
381  vigra_precondition(this->size() > 0,
382  "Polygon:.arcLengthQuantile(): polygon is empty.");
383  if(quantile == 0.0 || this->size() == 1)
384  return 0.0;
385  if(quantile == 1.0)
386  return this->size() - 1.0;
387  vigra_precondition(0.0 < quantile && quantile < 1.0,
388  "Polygon:.arcLengthQuantile(): quantile must be between 0 and 1.");
389  ArrayVector<double> arcLength;
390  arcLength.reserve(this->size());
391  arcLengthList(arcLength);
392  double length = quantile*arcLength.back();
393  unsigned int k=0;
394  for(; k<this->size(); ++k)
395  if(arcLength[k] >= length)
396  break;
397  --k;
398  return k + (length - arcLength[k]) / (arcLength[k+1] - arcLength[k]);
399  }
400 
401  void swap(Polygon &rhs)
402  {
403  Base::swap(rhs);
404  std::swap(length_, rhs.length_);
405  std::swap(lengthValid_, rhs.lengthValid_);
406  std::swap(partialArea_, rhs.partialArea_);
407  std::swap(partialAreaValid_, rhs.partialAreaValid_);
408  }
409 
410  void reverse()
411  {
412  std::reverse(Base::begin(), Base::end());
413  if(partialAreaValid_)
414  partialArea_ = -partialArea_;
415  }
416 
417  POINT nearestPoint(const_reference p) const;
418 
419  Polygon & operator+=(POINT const & offset)
420  {
421  if(!closed())
422  partialAreaValid_ = false;
423  for(unsigned int i = 0; i < size(); ++i)
424  Base::operator[](i) += offset;
425  return *this;
426  }
427 
428  Polygon & operator-=(POINT const & offset)
429  {
430  if(!closed())
431  partialAreaValid_ = false;
432  for(unsigned int i = 0; i < size(); ++i)
433  Base::operator[](i) -= offset;
434  return *this;
435  }
436 
437  Polygon & operator*=(double scale)
438  {
439  partialArea_ *= sq(scale);
440  length_ *= scale;
441  for(unsigned int i = 0; i < size(); ++i)
442  Base::operator[](i) *= scale;
443  return *this;
444  }
445 
446  Polygon & operator/=(double scale)
447  {
448  partialArea_ /= sq(scale);
449  length_ /= scale;
450  for(unsigned int i = 0; i < size(); ++i)
451  Base::operator[](i) /= scale;
452  return *this;
453  }
454 
455  bool operator==(Polygon const & rhs) const
456  {
457  if(size() != rhs.size())
458  return false;
459  for(size_type k=0; k<size(); ++k)
460  if((*this)[k] != rhs[k])
461  return false;
462  return true;
463  }
464 
465  bool operator!=(Polygon const & rhs) const
466  {
467  return !((*this) == rhs);
468  }
469 
470  protected:
471 
472  mutable double length_;
473  mutable bool lengthValid_;
474  mutable double partialArea_;
475  mutable bool partialAreaValid_;
476 };
477 
478 template <class POINT>
479 POINT Polygon<POINT>::nearestPoint(const_reference p) const
480 {
481  double dist = NumericTraits<double>::max();
482  POINT r;
483  for(unsigned int k=1; k<size(); ++k)
484  {
485  POINT dp = (*this)[k] - (*this)[k-1];
486  POINT dc = p - (*this)[k-1];
487  double t = dot(dp, dc);
488  if(t != 0.0)
489  t /= squaredNorm(dp);
490  if(t > 1.0)
491  {
492  double d = norm((*this)[k]-p);
493  if (d < dist)
494  {
495  dist = d;
496  r = (*this)[k];
497  }
498  }
499  else if(t < 0.0)
500  {
501  double d = norm((*this)[k-1]-p);
502  if (d < dist)
503  {
504  dist = d;
505  r = (*this)[k-1];
506  }
507  }
508  else
509  {
510  POINT pp = (*this)[k-1] + t*dp;
511  double d = norm(pp-p);
512  if (d < dist)
513  {
514  dist = d;
515  r = pp;
516  }
517  }
518  }
519  return r;
520 }
521 
522 template <class POINT>
523 bool
524 Polygon<POINT>::contains(const_reference point,
525  coordinate_type tolerance) const
526 {
527  typedef typename Polygon<POINT>::Base Base;
528  vigra_precondition(closed(),
529  "Polygon::contains() requires polygon to be closed!");
530 
531  // NOTE: the following code is very similar to detail::createScanIntervals()
532  // to ensure consistent results.
533 
534  Polygon p = (*this) - point; // shift the polygon so that we only need to test
535  // for intersections with scanline 0
536  int n = p.size();
537  for(int k=0; k<n; ++k)
538  if(closeAtTolerance(p[k][1], 0.0, tolerance))
539  ((Base&)p)[k][1] = 0.0;
540 
541  int result = 0;
542  bool drop_next_start_point = false;
543  int first_point_maybe_dropped = -1;
544  for(int k=0; k<n-1; ++k)
545  {
546  Point const & p1 = p[k];
547  Point const & p2 = p[k+1];
548 
549  if(p1[1] == p2[1]) // ignore horizontal lines
550  continue;
551 
552  double t = (p2[0] - p1[0]) / (p2[1] - p1[1]);
553  double y, yend, dy;
554  if(p1[1] < p2[1])
555  {
556  y = ceil(p1[1]);
557  yend = floor(p2[1]);
558  dy = 1.0;
559  }
560  else
561  {
562  y = floor(p1[1]);
563  yend = ceil(p2[1]);
564  dy = -1.0;
565  }
566  if(yend != p2[1])
567  yend += dy;
568  if(drop_next_start_point)
569  {
570  y += dy;
571  drop_next_start_point = false;
572  }
573  if(first_point_maybe_dropped == -1)
574  {
575  if(y == 0.0 && p1[0] - p1[1]*t < 0.0)
576  first_point_maybe_dropped = 1;
577  else
578  first_point_maybe_dropped = 0;
579  }
580  if(y*dy <= 0.0 && yend*dy > 0.0) // intersects scanline 0
581  {
582  double x = p1[0] - p1[1]*t;
583  if(closeAtTolerance(x, 0.0, tolerance))
584  return true;
585  if(x < 0.0)
586  ++result;
587  }
588  else if(p2[1] == 0.0) // degenerate case
589  {
590  int j = (k+2)%n;
591  bool convex = detail::orderedClockwise(p1, p2, p[j]);
592  if(convex)
593  {
594  double x = p2[0] - p2[1]*t;
595  if(closeAtTolerance(x, 0.0, tolerance))
596  return true;
597  if(x < 0.0)
598  ++result;
599  }
600  for(; j != k+1; j = (j+1)%n)
601  {
602  double bend = dy*(p[j][1] - yend);
603  if(bend == 0.0)
604  continue;
605  // Drop startpoint of next segment when the polygon after a convex
606  // degenerate knot eventually crosses the scanline, or when it
607  // returns to the original side of the scanline after a concave
608  // degenerate knot.
609  if((convex && bend > 0.0) || (!convex && bend < 0.0))
610  drop_next_start_point = true;
611  break;
612  }
613  }
614  }
615 
616  if(drop_next_start_point && first_point_maybe_dropped == 1)
617  --result;
618 
619  return (result % 2) != 0;
620 }
621 
622 template <class POINT>
623 inline Polygon<POINT> round(Polygon<POINT> const & p)
624 {
625  Polygon<POINT> result(p.size());
626  for(unsigned int i = 0; i < p.size(); ++i)
627  {
628  result.setPointUnsafe(i, round(p[i]));
629  }
630  return result;
631 }
632 
633 template <class POINT>
634 inline Polygon<TinyVector<std::ptrdiff_t, 2> > roundi(Polygon<POINT> const & p)
635 {
636  Polygon<TinyVector<std::ptrdiff_t, 2> > result(p.size());
637  for(unsigned int i = 0; i < p.size(); ++i)
638  {
639  result.setPointUnsafe(i,roundi(p[i]));
640  }
641  return result;
642 }
643 
644 template <class POINT>
645 inline Polygon<POINT>
646 operator+(Polygon<POINT> const & p, POINT const & offset)
647 {
648  return Polygon<POINT>(p) += offset;
649 }
650 
651 template <class POINT>
652 inline Polygon<POINT>
653 operator+(POINT const & offset, Polygon<POINT> const & p)
654 {
655  return Polygon<POINT>(p) += offset;
656 }
657 
658 template <class POINT>
659 inline Polygon<POINT>
660 operator-(Polygon<POINT> const & p)
661 {
662  Polygon<POINT> result(p.size());
663  for(unsigned int i = 0; i < p.size(); ++i)
664  result.setPointUnsafe(i, -p[i]);
665  return result;
666 }
667 
668 template <class POINT>
669 inline Polygon<POINT>
670 operator-(Polygon<POINT> const & p, POINT const & offset)
671 {
672  return Polygon<POINT>(p) -= offset;
673 }
674 
675 template <class POINT>
676 inline Polygon<POINT>
677 operator*(Polygon<POINT> const & p, double scale)
678 {
679  return Polygon<POINT>(p) *= scale;
680 }
681 
682 template <class POINT>
683 inline Polygon<POINT>
684 operator*(double scale, Polygon<POINT> const & p)
685 {
686  return Polygon<POINT>(p) *= scale;
687 }
688 
689 
690 template <class POINT>
691 inline Polygon<POINT>
692 operator/(Polygon<POINT> const & p, double scale)
693 {
694  return Polygon<POINT>(p) /= scale;
695 }
696 
697 template <class POINT>
698 inline Polygon<POINT>
699 transpose(Polygon<POINT> const & p)
700 {
701  Polygon<POINT> result(p.size());
702  for(unsigned int i = 0; i < p.size(); ++i)
703  {
704  result.setPointUnsafe(i, POINT(p[i][1], p[i][0]));
705  }
706  return result;
707 }
708 
709 template <class POINT>
710 inline Polygon<POINT>
711 reverse(Polygon<POINT> const & p)
712 {
713  Polygon<POINT> result(p);
714  result.reverse();
715  return result;
716 }
717 
718 template<class Point>
719 Point centroid(const Polygon<Point> &polygon)
720 {
721  vigra_precondition(polygon.closed(),
722  "centroid() expects a closed polygon");
723  double a = 0.0;
724  TinyVector<double, 2> result;
725  for(unsigned int i = 1; i < polygon.size(); ++i)
726  {
727  double pa = (polygon[i-1][0]*polygon[i][1] -
728  polygon[i-1][1]*polygon[i][0]);
729  a += pa;
730  result += (polygon[i-1] + polygon[i])*pa;
731  }
732  return result / (3.0*a);
733 }
734 
735 } // namespace vigra
736 
737 /********************************************************************/
738 
739 namespace std {
740 
741 template<class T>
742 void swap(vigra::Polygon<T> &a, vigra::Polygon<T> &b)
743 {
744  a.swap(b);
745 }
746 
747 } // namespace std
748 
749 /********************************************************************/
750 
751 namespace vigra {
752 
753 /** \brief Create a polygon from the interpixel contour of a labeled region.
754 
755  The point \a anchor_point must be in the region whose contour we want to extract,
756  and must be adjacent to the contour. The algorithm uses the 'left hand on the wall'
757  algorithm to trace the connected component whose label equals the label of the
758  \a anchor_point. The contour is returned in \a countour_points as a closed polygon
759  that circles the region counter-clockwise in the image coordinate system (i.e. the
760  coordinate system where x points to the right and y points downwards). Since the
761  resulting polygon represents the interpixel contour, all points will have one integer
762  and one half-integer coordinate.
763 */
764 template<class T, class S, class PointArray>
765 void
767  Shape2 const & anchor_point,
768  PointArray & contour_points)
769 {
770  typedef typename PointArray::value_type Point;
771 
772  Shape2 step[4] = { Shape2(0, -1), Shape2(1, 0), Shape2(0, 1), Shape2(-1, 0) };
773  Point contour_offsets[4] = { Point(-0.5, 0), Point(0, -0.5), Point(0.5, 0), Point(0, 0.5) };
774 
775  T foreground = label_image[anchor_point];
776 
777  int direction;
778  Shape2 position;
779  // find a position outside the object from which we place the hand
780  for(direction = 3; direction >= 0; --direction)
781  {
782  position = anchor_point + step[(direction + 1) % 4];
783  if(!label_image.isInside(position) || label_image[position] != foreground)
784  break;
785  }
786 
787  vigra_precondition(direction >= 0,
788  "extractContour(): the anchor point must be at the region border.");
789 
790  int initial_direction = direction;
791  Shape2 initial_position = position;
792 
793  // go around the object
794  do
795  {
796  contour_points.push_back(position + contour_offsets[direction]);
797 
798  Shape2 next_position = position + step[direction];
799 
800  if(label_image.isInside(next_position) &&
801  label_image[next_position] == foreground)
802  {
803  // we have bumped into a wall => turn right to touch the wall again
804  direction = (direction + 1) % 4;
805  }
806  else
807  {
808  position = next_position;
809  int next_direction = (direction + 3) % 4;
810  next_position += step[next_direction];
811  if(!label_image.isInside(next_position) ||
812  label_image[next_position] != foreground)
813  {
814  // we have lost the wall => turn left and move forward to touch the wall again
815  direction = next_direction;
816  position = next_position;
817  }
818  }
819  }
820  while (position != initial_position || direction != initial_direction);
821 
822  contour_points.push_back(contour_points.front()); // make it a closed polygon
823 }
824 
825 /** \brief Compute convex hull of a 2D polygon.
826 
827  The input array \a points contains a (not necessarily ordered) set of 2D points
828  whose convex hull is to be computed. The array's <tt>value_type</tt> (i.e. the point type)
829  must be compatible with std::vector (in particular, it must support indexing,
830  copying, and have <tt>size() == 2</tt>). The points of the convex hull will be appended
831  to the output array \a convex_hull (which must support <tt>std::back_inserter(convex_hull)</tt>).
832  Since the convex hull is a closed polygon, the first and last point of the output will
833  be the same (i.e. the first point will simply be inserted at the end again). The points
834  of the convex hull will be ordered counter-clockwise, starting with the leftmost point
835  of the input. The function implements Andrew's Monotone Chain algorithm.
836 */
837 template<class PointArray1, class PointArray2>
838 void convexHull(const PointArray1 &points, PointArray2 & convex_hull)
839 {
840  vigra_precondition(points.size() >= 2,
841  "convexHull(): at least two input points are needed.");
842  vigra_precondition(points[0].size() == 2,
843  "convexHull(): 2-dimensional points required.");
844 
845  typedef typename PointArray1::value_type Point;
846 
847  typename PointArray1::const_iterator begin = points.begin();
848  if(points.front() == points.back()) // closed polygon
849  ++begin; // => remove redundant start point
850  ArrayVector<Point> ordered(begin, points.end());
851  std::sort(ordered.begin(), ordered.end(), detail::pointYXOrdering<Point>);
852 
854 
855  int n = ordered.size(), k=0;
856 
857  // Build lower hull
858  for (int i = 0; i < n; i++)
859  {
860  while (k >= 2 && detail::orderedClockwise(H[k-2], H[k-1], ordered[i]))
861  {
862  H.pop_back();
863  --k;
864  }
865  H.push_back(ordered[i]);
866  ++k;
867  }
868 
869  // Build upper hull
870  for (int i = n-2, t = k+1; i >= 0; i--)
871  {
872  while (k >= t && detail::orderedClockwise(H[k-2], H[k-1], ordered[i]))
873  {
874  H.pop_back();
875  --k;
876  }
877  H.push_back(ordered[i]);
878  ++k;
879  }
880 
881  for(int i=k-1; i>=0; --i)
882  convex_hull.push_back(H[i]);
883 }
884 
885 /********************************************************************/
886 /* */
887 /* polygon drawing */
888 /* */
889 /********************************************************************/
890 
891 namespace detail {
892 
893 /*
894  * Find and sort all intersection points of the polygon with scanlines.
895  * Polygons are considered as closed set, i.e. pixels on the polygon
896  * contour are included. The function handles degenerate cases (i.e.
897  * knots on scanlines) correctly.
898  */
899 template<class Point, class Array>
900 void createScanIntervals(Polygon<Point> const &p, Array & result)
901 {
902  bool drop_next_start_point = false;
903  int n = p.size();
904  for(int k=0; k<n-1; ++k)
905  {
906  Point const & p1 = p[k];
907  Point const & p2 = p[k+1];
908 
909  if(p1[1] == p2[1]) // ignore horizontal lines
910  continue;
911 
912  double t = (p2[0] - p1[0]) / (p2[1] - p1[1]);
913  double y, yend, dy;
914  if(p1[1] < p2[1])
915  {
916  y = ceil(p1[1]);
917  yend = floor(p2[1]);
918  dy = 1.0;
919  }
920  else
921  {
922  y = floor(p1[1]);
923  yend = ceil(p2[1]);
924  dy = -1.0;
925  }
926  if(yend != p2[1]) // in general don't include the segment's endpoint
927  yend += dy; // (since it is also the start point of the next segment)
928  if(drop_next_start_point) // handle degeneracy from previous iteration
929  {
930  y += dy;
931  drop_next_start_point = false;
932  }
933  for(; (y-yend)*dy < 0.0; y += dy) // compute scanline intersections
934  {
935  double x = p1[0] + (y - p1[1])*t;
936  result.push_back(Point(x,y));
937  }
938  if(yend == p2[1]) // degenerate case: p2 is exactly on a scanline (yend is integer)
939  {
940  int j = (k+2)%n;
941  bool convex = detail::orderedClockwise(p1, p2, p[j]);
942  if(convex) // include the segment's endpoint p2 when it is a convex knot
943  {
944  result.push_back(p2);
945  }
946  for(; j != k+1; j = (j+1)%n)
947  {
948  double bend = dy*(p[j][1] - yend);
949  if(bend == 0.0)
950  continue;
951  // Drop startpoint of next segment when the polygon after a convex
952  // degenerate knot eventually crosses the scanline, or when it
953  // returns to the original side of the scanline after a concave
954  // degenerate knot.
955  if((convex && bend > 0.0) || (!convex && bend < 0.0))
956  drop_next_start_point = true;
957  break;
958  }
959  }
960  }
961 
962  if(drop_next_start_point)
963  result.erase(result.begin());
964 
965  vigra_invariant((result.size() & 1) == 0,
966  "createScanIntervals(): internal error - should return an even number of points.");
967  sort(result.begin(), result.end(), pointYXOrdering<Point>);
968 }
969 
970 
971 } // namespace detail
972 
973 template<class Point, class FUNCTOR>
974 bool
975 inspectPolygon(Polygon<Point> const &p,
976  FUNCTOR const & f)
977 {
978  vigra_precondition(p.closed(),
979  "inspectPolygon(): polygon must be closed (i.e. first point == last point).");
980 
981  std::vector<Point> scan_intervals;
982  detail::createScanIntervals(p, scan_intervals);
983 
984  for(unsigned int k=0; k < scan_intervals.size(); k+=2)
985  {
986  Shape2 p((MultiArrayIndex)ceil(scan_intervals[k][0]),
987  (MultiArrayIndex)scan_intervals[k][1]);
988  MultiArrayIndex xend = (MultiArrayIndex)floor(scan_intervals[k+1][0]) + 1;
989  for(; p[0] < xend; ++p[0])
990  if(!f(p))
991  return false;
992  }
993  return true;
994 }
995 
996 /** \brief Render closed polygon \a p into the image \a output_image.
997 
998  All pixels on the polygon's contour and in its interior are
999  set to the given \a value. Parts of the polygon outside the image
1000  region are clipped. The function uses a robust X-intersection array
1001  algorithm that is able to handle all corner cases (concave and
1002  self-intersecting polygons, knots on integer coordinates).
1003  */
1004 template<class Point, class T, class S, class Value>
1006  MultiArrayView<2, T, S> &output_image,
1007  Value value)
1008 {
1009  vigra_precondition(p.closed(),
1010  "fillPolygon(): polygon must be closed (i.e. first point == last point).");
1011 
1012  std::vector<Point> scan_intervals;
1013  detail::createScanIntervals(p, scan_intervals);
1014 
1015  for(unsigned int k=0; k < scan_intervals.size(); k+=2)
1016  {
1017  MultiArrayIndex x = (MultiArrayIndex)ceil(scan_intervals[k][0]),
1018  y = (MultiArrayIndex)scan_intervals[k][1],
1019  xend = (MultiArrayIndex)floor(scan_intervals[k+1][0]) + 1;
1020  vigra_invariant(y == scan_intervals[k+1][1],
1021  "fillPolygon(): internal error - scan interval should have same y value.");
1022  // clipping
1023  if(y < 0)
1024  continue;
1025  if(y >= output_image.shape(1))
1026  break;
1027  if(x < 0)
1028  x = 0;
1029  if(xend > output_image.shape(0))
1030  xend = output_image.shape(0);
1031  // drawing
1032  for(; x < xend; ++x)
1033  output_image(x,y) = value;
1034  }
1035 }
1036 
1037 #if 0
1038 
1039 // the following sophisticated polygon resampling functions have no tests yet
1040 
1041 /********************************************************************/
1042 
1043 template<bool useMaxStep, class PointIterator, class TargetArray>
1044 void simplifyPolygonHelper(
1045  const PointIterator &polyBegin, const PointIterator &polyEnd,
1046  TargetArray &simple, double epsilon,
1047  double maxStep2 = vigra::NumericTraits<double>::max())
1048 {
1049  if(polyEnd - polyBegin <= 2)
1050  return; // no splitpoint necessary / possible
1051 
1052  PointIterator splitPos(polyEnd), lastPoint(polyEnd);
1053  --lastPoint;
1054 
1055  double maxDist = epsilon;
1056 
1057  // calculate normal of straight end point connection
1058  typename TargetArray::value_type
1059  straight(*lastPoint - *polyBegin);
1060  double straightLength2 = straight.squaredMagnitude();
1061 
1062  // search splitpoint
1063  if(straightLength2 > 1e-16)
1064  {
1065  typename TargetArray::value_type
1066  normal(straight[1], -straight[0]);
1067 
1068  normal /= std::sqrt(straightLength2);
1069 
1070  // iterate over points *between* first and last point:
1071  PointIterator it(polyBegin);
1072  for(++it; it != lastPoint; ++it)
1073  {
1074  double dist = fabs(dot(*it - *polyBegin, normal));
1075  if(dist > maxDist)
1076  {
1077  splitPos = it;
1078  maxDist = dist;
1079  }
1080  }
1081  }
1082  else
1083  {
1084  // start- and end-points identical?! -> look for most distant point
1085  PointIterator it(polyBegin);
1086  for(++it; it != lastPoint; ++it)
1087  {
1088  double dist = (*it - *polyBegin).magnitude();
1089  if(dist > maxDist)
1090  {
1091  splitPos = it;
1092  maxDist = dist;
1093  }
1094  }
1095  }
1096 
1097  if(useMaxStep && (straightLength2 > maxStep2) && (splitPos == polyEnd))
1098  {
1099  PointIterator it(polyBegin);
1100  ++it;
1101  double bestD2D = std::fabs((*it - *polyBegin).squaredMagnitude()
1102  - (*it - *lastPoint).squaredMagnitude());
1103  splitPos = it;
1104  for(++it; it != lastPoint; ++it)
1105  {
1106  double dist2Diff = std::fabs((*it - *polyBegin).squaredMagnitude()
1107  - (*it - *lastPoint).squaredMagnitude());
1108  if(dist2Diff < bestD2D)
1109  {
1110  bestD2D = dist2Diff;
1111  splitPos = it;
1112  }
1113  }
1114  }
1115 
1116  if(splitPos != polyEnd)
1117  {
1118  simplifyPolygonHelper<useMaxStep>(
1119  polyBegin, splitPos + 1, simple, epsilon, maxStep2);
1120  simple.push_back(*splitPos);
1121  simplifyPolygonHelper<useMaxStep>(
1122  splitPos, polyEnd, simple, epsilon, maxStep2);
1123  }
1124 }
1125 
1126 template<class PointArray>
1127 void simplifyPolygon(
1128  const PointArray &poly, PointArray &simple, double epsilon)
1129 {
1130  simple.push_back(poly[0]);
1131  simplifyPolygonHelper<false>(poly.begin(), poly.end(), simple, epsilon);
1132  simple.push_back(poly[poly.size()-1]);
1133 }
1134 
1135 template<class PointArray>
1136 void simplifyPolygon(
1137  const PointArray &poly, PointArray &simple, double epsilon, double maxStep)
1138 {
1139  simple.push_back(poly[0]);
1140  simplifyPolygonHelper<true>(poly.begin(), poly.end(),
1141  simple, epsilon, maxStep*maxStep);
1142  simple.push_back(poly[poly.size()-1]);
1143 }
1144 
1145 /********************************************************************/
1146 
1147 namespace detail {
1148 
1149 template <class Point>
1150 Point digitalLineIntersection(TinyVector<double, 3> const & l1, TinyVector<double, 3> const & l2)
1151 {
1152  double d = l1[0]*l2[1] - l1[1]*l2[0];
1153  return Point((l1[1]*l2[2] - l1[2]*l2[1]) / d, (l1[2]*l2[0] - l1[0]*l2[2]) / d);
1154 }
1155 
1156 } // namespace detail
1157 
1158 template<class PointArray>
1159 void simplifyPolygonDigitalLine(
1160  const PointArray &poly, PointArray &simple, int connectivity)
1161 {
1162  typedef typename PointArray::value_type Point;
1163 
1164  int size = poly.size();
1165  if(size <= 2)
1166  {
1167  simple = poly;
1168  return;
1169  }
1170 
1171  vigra_precondition(connectivity == 4 || connectivity == 8,
1172  "simplifyPolygonDigitalLine(): connectivity must be 4 or 8.");
1173 
1174  bool isOpenPolygon = poly[size-1] != poly[0];
1175 
1176  ArrayVector<TinyVector<double, 3> > lines;
1177  Point l1 = poly[0],
1178  r1 = l1,
1179  l2 = poly[1],
1180  r2 = l2;
1181  double a = l2[1] - l1[1],
1182  b = l1[0] - l2[0],
1183  c = -a*l2[0] - b*l2[1];
1184  for(int k=2; k < size; ++k)
1185  {
1186  double ab = (connectivity == 4)
1187  ? std::fabs(a) + std::fabs(b)
1188  : std::max(std::fabs(a), std::fabs(b));
1189  double d = a*poly[k][0] + b*poly[k][1] + c;
1190  if(d < -1.0 || d > ab)
1191  {
1192  // finish current segment
1193  c = (c - a*r2[0] - b*r2[1]) / 2.0;
1194  lines.push_back(TinyVector<double, 3>(a, b, c));
1195  // initialize new segment
1196  l1 = poly[k-1];
1197  r1 = l1;
1198  l2 = poly[k];
1199  r2 = l2;
1200  a = l2[1] - l1[1];
1201  b = l1[0] - l2[0];
1202  c = -a*l2[0] - b*l2[1];
1203  }
1204  else if(d <= 0.0)
1205  {
1206  l2 = poly[k];
1207  if(d < 0.0)
1208  {
1209  r1 = r2;
1210  a = l2[1] - l1[1];
1211  b = l1[0] - l2[0];
1212  c = -a*l2[0] - b*l2[1];
1213  }
1214  }
1215  else if(d >= ab - 1.0)
1216  {
1217  r2 = poly[k];
1218  if(d > ab - 1.0)
1219  {
1220  l1 = l2;
1221  a = r2[1] - r1[1];
1222  b = r1[0] - r2[0];
1223  c = -a*l2[0] - b*l2[1];
1224  }
1225  }
1226  }
1227 
1228  c = (c - a*r2[0] - b*r2[1]) / 2.0;
1229  lines.push_back(TinyVector<double, 3>(a, b, c));
1230  int segments = lines.size();
1231 
1232  if(isOpenPolygon)
1233  simple.push_back(poly[0]);
1234  else
1235  simple.push_back(detail::digitalLineIntersection<Point>(lines[0], lines[segments-1]));
1236 
1237  for(int k=1; k<segments; ++k)
1238  simple.push_back(detail::digitalLineIntersection<Point>(lines[k-1], lines[k]));
1239 
1240  if(isOpenPolygon)
1241  simple.push_back(poly[size-1]);
1242  else
1243  simple.push_back(detail::digitalLineIntersection<Point>(lines[0], lines[segments-1]));
1244 }
1245 
1246 /********************************************************************/
1247 
1248 template<class PointArray>
1249 void resamplePolygon(
1250  const PointArray &poly, PointArray &simple, double desiredPointDistance)
1251 {
1252  typedef typename PointArray::value_type Point;
1253 
1254  int size = poly.size();
1255  bool isOpenPolygon = !poly.closed();
1256 
1257  ArrayVector<double> arcLength;
1258  poly.arcLengthList(arcLength);
1259  int segmentCount = int(std::ceil(arcLength[size-1] / desiredPointDistance));
1260  if(segmentCount < 2)
1261  {
1262  simple.push_back(poly[0]);
1263  if(!isOpenPolygon)
1264  {
1265  Point p = poly[1];
1266  double dist = (p - poly[0]).magnitude();
1267  for(int k=2; k < size-1; ++k)
1268  {
1269  double d = (poly[k] - poly[0]).magnitude();
1270  if(d > dist)
1271  {
1272  dist = d;
1273  p = poly[k];
1274  }
1275  }
1276  simple.push_back(p);
1277  }
1278  simple.push_back(poly[size-1]);
1279  return;
1280  }
1281 
1282  for(int k=0; k<size; ++k)
1283  arcLength[k] *= segmentCount / arcLength[size-1];
1284 
1285  ArrayVector<Point> integrals(segmentCount+1, Point(0.0, 0.0));
1286  Point p1 = poly[0];
1287  double t1 = 0.0;
1288  int l = 1;
1289  for(int k=1; k<size; ++k)
1290  {
1291  double d = arcLength[k];
1292  while(d >= l && l <= segmentCount)
1293  {
1294  double t2 = 1.0;
1295  double dt = t2 - t1;
1296  Point p2 = poly.interpolate(k-1, (l - arcLength[k-1]) / (d - arcLength[k-1]));
1297  Point sum1 = 0.5 * dt * (p1 + p2);
1298  Point sumt = dt / 6.0 * (p1*(2.0*t1+t2) + p2*(t1+2.0*t2));
1299  integrals[l-1] += sum1 - sumt;
1300  integrals[l] += sumt;
1301  if(isOpenPolygon && l==1)
1302  {
1303  integrals[0] += poly[0] - integrals[1];
1304  }
1305  p1 = p2;
1306  t1 = 0.0;
1307  ++l;
1308  if(isOpenPolygon && l==segmentCount)
1309  {
1310  integrals[segmentCount] += integrals[segmentCount-1];
1311  }
1312  }
1313  if(d < l && l <= segmentCount)
1314  {
1315  double t2 = std::fmod(d, 1.0);
1316  double dt = t2 - t1;
1317  Point p2 = poly[k];
1318  Point sum1 = 0.5 * dt * (p1 + p2);
1319  Point sumt = dt / 6.0 * (p1*(2.0*t1+t2) + p2*(t1+2.0*t2));
1320  integrals[l-1] += sum1 - sumt;
1321  integrals[l] += sumt;
1322  p1 = p2;
1323  t1 = t2;
1324  }
1325  }
1326 
1327  if(isOpenPolygon)
1328  {
1329  integrals[segmentCount] += poly[size-1] - integrals[segmentCount-1];
1330  integrals[1] -= integrals[0] / 6.0;
1331  integrals[segmentCount-1] -= integrals[segmentCount] / 6.0;
1332 
1333  ArrayVector<double> g(segmentCount);
1334  double b = 2.0 / 3.0;
1335  simple.push_back(poly[0]);
1336  simple.push_back(integrals[1] / b);
1337  for(int k=2; k<segmentCount; ++k)
1338  {
1339  g[k] = 1.0 / 6.0 / b;
1340  b = 2.0 / 3.0 - g[k] / 6.0;
1341  simple.push_back((integrals[k] - simple[k-1] / 6.0) / b);
1342  }
1343  for(int k = segmentCount-2; k >= 1; --k)
1344  {
1345  simple[k] -= g[k+1] * simple[k+1];
1346  }
1347 
1348  simple.push_back(poly[size-1]);
1349  }
1350  else
1351  {
1352  integrals[0] += integrals[segmentCount];
1353 
1354  int initializationSteps = std::min(segmentCount, 5);
1355  ArrayVector<Point> p(segmentCount+2*initializationSteps);
1356  double b = 0.6220084679281461,
1357  g = 0.26794919243112275;
1358  p[0] = integrals[0] / b;
1359 
1360  for(int k=1; k<segmentCount+2*initializationSteps; ++k)
1361  {
1362  p[k] = (integrals[k % segmentCount] - p[k-1] / 6.0) / b;
1363  }
1364  for(int k = segmentCount+2*initializationSteps-2; k >= initializationSteps; --k)
1365  {
1366  p[k] -= g * p[k+1];
1367  }
1368 
1369  for(int k=segmentCount; k<segmentCount+initializationSteps; ++k)
1370  simple.push_back(p[k]);
1371  for(int k=initializationSteps; k<=segmentCount; ++k)
1372  simple.push_back(p[k]);
1373  }
1374 }
1375 
1376 /********************************************************************/
1377 
1378 template<class PointArray>
1379 void resamplePolygonLinearInterpolation(
1380  const PointArray &poly, PointArray &simple, double desiredPointDistance)
1381 {
1382  int size = poly.size();
1383  if(size <= 2)
1384  {
1385  simple = poly;
1386  return;
1387  }
1388 
1389  ArrayVector<double> arcLengths;
1390  poly.arcLengthList(arcLengths);
1391 
1392  int steps = int(std::ceil(arcLengths[size-1] / desiredPointDistance));
1393  double newStep = arcLengths[size-1] / steps;
1394 
1395  simple.push_back(poly[0]);
1396  int l = 1;
1397  for(int k=1; k<steps; ++k)
1398  {
1399  double currentArcLength = k*newStep;
1400  for(; l < size; ++l)
1401  {
1402  if(arcLengths[l] >= currentArcLength)
1403  break;
1404  }
1405  double o = (arcLengths[l] - currentArcLength) / (arcLengths[l] - arcLengths[l-1]);
1406  simple.push_back(o*poly[l-1] + (1.0-o)*poly[l]);
1407  }
1408  simple.push_back(poly[size-1]);
1409 }
1410 
1411 /********************************************************************/
1412 
1413 template<class PointArray>
1414 void resamplePolygonExponentialFilter(
1415  const PointArray &poly, PointArray &simple, double scale, double desiredPointDistance)
1416 {
1417  int size = poly.size();
1418  if(size <= 2)
1419  {
1420  simple = poly;
1421  return;
1422  }
1423 
1424  bool isOpenPolygon = !poly.closed();
1425 
1426  typedef typename PointArray::value_type Point;
1427  ArrayVector<Point> pforward(size), pbackward(size);
1428  ArrayVector<double> wforward(size), wbackward(size), weights(size-1);
1429  for(int k=0; k < size - 1; ++k)
1430  weights[k] = std::exp(-(poly[k] - poly[k+1]).magnitude()/scale);
1431 
1432  // init recursion with cyclic boundary conditions
1433  Point p = poly[0];
1434  double w = 1.0;
1435  for(int k=1; k < size; ++k)
1436  {
1437  p = poly[k] + weights[k-1]*p;
1438  w = 1.0 + weights[k-1]*w;
1439  }
1440  pforward[0] = p;
1441  wforward[0] = w;
1442 
1443  p = poly[size-1];
1444  w = 1.0;
1445  for(int k=size-2; k>=0; --k)
1446  {
1447  p = poly[k] + weights[k]*p;
1448  w = 1.0 + weights[k]*w;
1449  }
1450  pbackward[size-1] = p;
1451  wbackward[size-1] = w;
1452 
1453  if(isOpenPolygon)
1454  {
1455  // change initialization into anti-reflective boundary conditions for open polygons
1456  std::swap(wbackward[size-1], wforward[0]);
1457  std::swap(pbackward[size-1], pforward[0]);
1458  pforward[0] = 2.0*wforward[0]*poly[0] - pforward[0];
1459  pbackward[size-1] = 2.0*wbackward[size-1]*poly[size-1] - pbackward[size-1];
1460  }
1461 
1462  // forward and backward pass of the recursive filter
1463  for(int k=1; k < size; ++k)
1464  {
1465  pforward[k] = poly[k] + weights[k-1]*pforward[k-1];
1466  wforward[k] = 1.0 + weights[k-1]*wforward[k-1];
1467  }
1468  for(int k=size-2; k >= 0; --k)
1469  {
1470  pbackward[k] = poly[k] + weights[k]*pbackward[k+1];
1471  wbackward[k] = 1.0 + weights[k]*wbackward[k+1];
1472  }
1473 
1474  // measure the arc length of the new polygon (after possible shrinkage)
1475  p = (pforward[0]+weights[0]*pbackward[1]) / (wforward[0] + weights[0]*wbackward[1]);
1476  simple.push_back(p);
1477 
1478  Point pend = isOpenPolygon
1479  ? (weights[size-2]*pforward[size-2]+pbackward[size-1]) /
1480  (weights[size-2]*wforward[size-2] + wbackward[size-1])
1481  : p;
1482 
1483  ArrayVector<double> arcLength;
1484  double length = 0.0;
1485  arcLength.push_back(length);
1486  for(int k=1; k<size-1; ++k)
1487  {
1488  Point pc = (pforward[k]+weights[k]*pbackward[k+1]) / (wforward[k] + weights[k]*wbackward[k+1]);
1489  length += (pc - p).magnitude();
1490  arcLength.push_back(length);
1491  p = pc;
1492  }
1493  length += (p-pend).magnitude();
1494  arcLength.push_back(length);
1495 
1496 // alternative: use the arc lenth of the original polygon
1497 // poly.arcLengthList(arcLength);
1498 
1499  int steps = int(std::floor(arcLength[size-1] / desiredPointDistance+0.5));
1500  double newStep = arcLength[size-1] / steps;
1501 
1502  int l = 1;
1503  for(int k=1; k < steps; ++k)
1504  {
1505  double currentArcLength = k*newStep;
1506  for(; l < size; ++l)
1507  {
1508  if(arcLength[l] >= currentArcLength)
1509  break;
1510  }
1511  double w = weights[l-1];
1512  double o = (arcLength[l] - currentArcLength) / (arcLength[l] - arcLength[l-1]);
1513  double wl = std::pow(w, 1.0-o);
1514  double wr = std::pow(w, o);
1515  simple.push_back((wl*pforward[l-1]+wr*pbackward[l]) / (wl*wforward[l-1] + wr*wbackward[l]));
1516  }
1517  simple.push_back(pend);
1518 }
1519 
1520 /********************************************************************/
1521 
1522 namespace detail {
1523 
1524 template<class ArcLengthList, class PointList>
1525 typename PointList::value_type
1526 singleGaussianConvolvePolygonReflective(
1527  const ArcLengthList &arcLengthList,
1528  const PointList &pointList,
1529  int i, double arcLengthPos,
1530  const Gaussian<double> &g)
1531 {
1532  typedef typename PointList::value_type ValueType;
1533 
1534  ValueType sum(vigra::NumericTraits<ValueType>::zero());
1535  double norm = 0.0;
1536 
1537  int size = arcLengthList.size(),
1538  lastIndex = size - 1;
1539  double reflectLength = 2.0*arcLengthList[lastIndex];
1540 
1541  ValueType reflectPoint = 2.0*pointList[lastIndex];
1542  for(int j = i; true; ++j)
1543  {
1544  int k = (j > lastIndex)
1545  ? 2*lastIndex - j
1546  : j;
1547  double pos = arcLengthList[k];
1548  ValueType point = pointList[k];
1549  if(j > lastIndex)
1550  {
1551  pos = reflectLength - pos;
1552  point = reflectPoint - point;
1553  }
1554  double diff = pos - arcLengthPos;
1555  if(diff > g.radius())
1556  break;
1557  double w(g(diff));
1558  sum += w*point;
1559  norm += w;
1560  }
1561 
1562  reflectPoint = 2.0*pointList[0];
1563  for(int j = i - 1; true; --j)
1564  {
1565  int k = std::abs(j);
1566  double pos = arcLengthList[k];
1567  ValueType point = pointList[k];
1568  if(j < 0)
1569  {
1570  pos = -pos;
1571  point = reflectPoint - point;
1572  }
1573  double diff = pos - arcLengthPos;
1574  if(diff < -g.radius())
1575  break;
1576  double w(g(diff));
1577  sum += w*point;
1578  norm += w;
1579  }
1580 
1581  return sum / norm;
1582 }
1583 
1584 template<class ArcLengthList, class PointList>
1585 typename PointList::value_type
1586 singleGaussianConvolvePolygonCyclic(
1587  const ArcLengthList &arcLengthList,
1588  const PointList &pointList,
1589  int i, double arcLengthPos,
1590  const Gaussian<double> &g)
1591 {
1592  typedef typename PointList::value_type ValueType;
1593 
1594  ValueType sum(vigra::NumericTraits<ValueType>::zero());
1595  double norm = 0.0;
1596 
1597  int size = arcLengthList.size() - 1,
1598  lastIndex = size - 1;
1599  double totalLength = arcLengthList[size];
1600 
1601  for(int j = i; true; ++j)
1602  {
1603  int k = j % size;
1604  double pos = j > lastIndex
1605  ? arcLengthList[k] + totalLength
1606  : arcLengthList[k];
1607  ValueType point = pointList[k];
1608  double diff = pos - arcLengthPos;
1609  if(diff > g.radius())
1610  break;
1611  double w(g(diff));
1612  sum += w*point;
1613  norm += w;
1614  }
1615 
1616  for(int j = i - 1; true; --j)
1617  {
1618  int k = (j + size) % size;
1619  double pos = j < 0
1620  ? arcLengthList[k] - totalLength
1621  : arcLengthList[k];
1622  ValueType point = pointList[k];
1623  double diff = pos - arcLengthPos;
1624  if(diff < -g.radius())
1625  break;
1626  double w(g(diff));
1627  sum += w*point;
1628  norm += w;
1629  }
1630 
1631  return sum / norm;
1632 }
1633 
1634 } // namespace detail
1635 
1636 template<class PointArray>
1637 void resamplePolygonGaussianFilter(
1638  const PointArray &poly, PointArray &simple, double scale, double desiredPointDistance)
1639 {
1640  int size = poly.size();
1641  if(size <= 2)
1642  {
1643  simple = poly;
1644  return;
1645  }
1646 
1647  ArrayVector<double> arcLengths;
1648  poly.arcLengthList(arcLengths);
1649 
1650  Gaussian<double> g(scale);
1651 
1652  vigra_precondition(arcLengths[size-1] > g.radius(),
1653  "resamplePolygonGaussianFilter(): Filter longer than polygon.");
1654 
1655  bool isOpenPolygon = !poly.closed();
1656 
1657  int steps = int(std::ceil(arcLengths[size-1] / desiredPointDistance));
1658  double newStep = arcLengths[size-1] / steps;
1659 
1660  int l = 0;
1661  for(int k=0; k<steps; ++k)
1662  {
1663  double currentArcLength = k*newStep;
1664  for(; l < size; ++l)
1665  {
1666  if(arcLengths[l] >= currentArcLength)
1667  break;
1668  }
1669  if(isOpenPolygon)
1670  simple.push_back(detail::singleGaussianConvolvePolygonReflective(arcLengths, poly, l, currentArcLength, g));
1671  else
1672  simple.push_back(detail::singleGaussianConvolvePolygonCyclic(arcLengths, poly, l, currentArcLength, g));
1673  }
1674  if(isOpenPolygon)
1675  simple.push_back(detail::singleGaussianConvolvePolygonReflective(arcLengths, poly, size-1, arcLengths[size-1], g));
1676  else
1677  simple.push_back(simple[0]);
1678 }
1679 
1680 /********************************************************************/
1681 
1682 namespace detail {
1683 
1684 template<class Point>
1685 Point spline3Integral(Point const & p1, Point const & p2, double t1, double t2)
1686 {
1687  StaticPolynomial<5, double> p[2];
1688  p[0][0] = p[1][0] = 0.0;
1689  if(t1 >= 1.0)
1690  {
1691  return (t1 - t2) / 120.0 *
1692  (p1 * (-80.0 + t1*(80.0 + 2.0*t2*(t2 - 10.0) + t1*(3.0*t2 - 30.0 + 4.0*t1)) + t2*(40.0 + t2*(t2 - 10.0))) +
1693  p2 * (-80.0 + t1*(40.0 + t2*(3.0*t2 - 20.0) + t1*(2.0*t2 - 10.0 + t1)) + t2*(80.0 + t2*(4.0*t2 - 30.0))));
1694  }
1695  else
1696  {
1697  return (t2 - t1) / 120.0 *
1698  (p1 * (40.0 + t1*(2.0*t2*(3.0*t2 - 10.0) + t1*(9.0*t2 - 30.0 + 12.0*t1)) + t2*t2*(3.0*t2 - 10.0)) +
1699  p2 * (40.0 + t1*(t2*(9.0*t2 - 20.0) + t1*(6.0*t2 - 10.0 + 3.0*t1)) + t2*t2*(12.0*t2 - 30.0)));
1700  }
1701 }
1702 
1703 template<class ArcLengthList, class PointList>
1704 typename PointList::value_type
1705 singleSpline3ConvolvePolygon(
1706  const ArcLengthList &arcLengthList,
1707  const PointList &pointList,
1708  int left, int center, int right)
1709 {
1710  typedef typename PointList::value_type ValueType;
1711 
1712  ValueType sum(vigra::NumericTraits<ValueType>::zero());
1713  double arcLengthPos = arcLengthList[center];
1714  for(int j = center + 1; j <= right; ++j)
1715  {
1716  double t1 = arcLengthList[j-1] - arcLengthPos,
1717  t2 = arcLengthList[j] - arcLengthPos;
1718  sum += spline3Integral(pointList[j-1], pointList[j], t1, t2);
1719  }
1720  for(int j = center - 1; j >= left; --j)
1721  {
1722  double t1 = arcLengthPos - arcLengthList[j+1],
1723  t2 = arcLengthPos - arcLengthList[j];
1724  sum -= spline3Integral(-pointList[j+1], -pointList[j], t1, t2);
1725  }
1726 
1727  return sum;
1728 }
1729 
1730 } // namespace detail
1731 
1732 template<class PointArray>
1733 void polygonSplineControlPoints(
1734  const PointArray &poly, PointArray &splinePoints, int segmentCount)
1735 {
1736  typedef typename PointArray::value_type Point;
1737 
1738  int size = poly.size();
1739  vigra_precondition(size >= 4,
1740  "polygonSplineControlPoints(): Polygon must have at least 4 points.");
1741 
1742  bool isOpenPolygon = !poly.closed();
1743 
1744  ArrayVector<double> arcLength;
1745  poly.arcLengthList(arcLength);
1746  double totalLength = segmentCount / arcLength[size-1];
1747  for(int k=0; k<size; ++k)
1748  arcLength[k] *= totalLength;
1749 
1750  PointArray augmentedPoly;
1751  augmentedPoly.push_back(poly[0]);
1752 
1753  ArrayVector<double> augmentedArcLength;
1754  augmentedArcLength.push_back(0.0);
1755 
1756  ArrayVector<int> splineIndices(segmentCount + 1);
1757  splineIndices[0] = 0;
1758  int l = 1;
1759  for(int k=1; k<size-1; ++k)
1760  {
1761  double d = arcLength[k];
1762  while(d > l)
1763  {
1764  augmentedPoly.push_back(poly.interpolate(k-1, (l - arcLength[k-1]) / (d - arcLength[k-1])));
1765  augmentedArcLength.push_back(l);
1766  splineIndices[l] = augmentedPoly.size()-1;
1767  ++l;
1768  }
1769  augmentedPoly.push_back(poly[k]);
1770  augmentedArcLength.push_back(d);
1771  if(d == l)
1772  {
1773  splineIndices[l] = augmentedPoly.size()-1;
1774  ++l;
1775  }
1776  }
1777  augmentedPoly.push_back(poly[size-1]);
1778  augmentedArcLength.push_back(segmentCount);
1779  splineIndices[segmentCount] = augmentedPoly.size()-1;
1780  size = augmentedPoly.size();
1781 
1782  ArrayVector<Point> integrals(segmentCount+1);
1783  if(isOpenPolygon)
1784  {
1785  integrals[0] = augmentedPoly[0];
1786  PointArray reflectedPoly;
1787  Point reflectPoint = 2.0*poly[0];
1788  ArrayVector<double> reflectedArcLength;
1789  for(int k=-splineIndices[1]; k <= splineIndices[3]; ++k)
1790  {
1791  if(k < 0)
1792  {
1793  reflectedPoly.push_back(reflectPoint - augmentedPoly[-k]);
1794  reflectedArcLength.push_back(-augmentedArcLength[-k]);
1795  }
1796  else
1797  {
1798  reflectedPoly.push_back(augmentedPoly[k]);
1799  reflectedArcLength.push_back(augmentedArcLength[k]);
1800  }
1801  }
1802  integrals[1] = detail::singleSpline3ConvolvePolygon(reflectedArcLength, reflectedPoly,
1803  0, 2*splineIndices[1], splineIndices[1] + splineIndices[3]);
1804 
1805  reflectPoint = 2.0*augmentedPoly[size-1];
1806  for(int k=size-2; k>=splineIndices[segmentCount-1]; --k)
1807  {
1808  augmentedPoly.push_back(reflectPoint - augmentedPoly[k]);
1809  augmentedArcLength.push_back(2.0*segmentCount - augmentedArcLength[k]);
1810  }
1811  integrals[segmentCount-1] = detail::singleSpline3ConvolvePolygon(augmentedArcLength, augmentedPoly,
1812  splineIndices[segmentCount-3], splineIndices[segmentCount-1],
1813  2*splineIndices[segmentCount] - splineIndices[segmentCount-1]);
1814  integrals[segmentCount] = augmentedPoly[size-1];
1815  }
1816  else
1817  {
1818  PointArray wrappedPoly;
1819  ArrayVector<double> wrappedArcLength;
1820  for(int k=splineIndices[segmentCount-1]; k < splineIndices[segmentCount]; ++k)
1821  {
1822  wrappedPoly.push_back(augmentedPoly[k]);
1823  wrappedArcLength.push_back(augmentedArcLength[k] - segmentCount);
1824  }
1825  int indexShift = wrappedPoly.size();
1826  for(int k=0; k <= splineIndices[3]; ++k)
1827  {
1828  wrappedPoly.push_back(augmentedPoly[k]);
1829  wrappedArcLength.push_back(augmentedArcLength[k]);
1830  }
1831  integrals[1] = detail::singleSpline3ConvolvePolygon(wrappedArcLength, wrappedPoly,
1832  0, splineIndices[1] + indexShift, splineIndices[3] + indexShift);
1833 
1834  for(int k=1; k <= splineIndices[2]; ++k)
1835  {
1836  augmentedPoly.push_back(augmentedPoly[k]);
1837  augmentedArcLength.push_back(segmentCount + augmentedArcLength[k]);
1838  }
1839  integrals[segmentCount-1] = detail::singleSpline3ConvolvePolygon(augmentedArcLength, augmentedPoly,
1840  splineIndices[segmentCount-3], splineIndices[segmentCount-1],
1841  splineIndices[segmentCount] + splineIndices[1]);
1842  integrals[0] = detail::singleSpline3ConvolvePolygon(augmentedArcLength, augmentedPoly,
1843  splineIndices[segmentCount-2], splineIndices[segmentCount],
1844  splineIndices[segmentCount] + splineIndices[2]);
1845  }
1846 
1847  for(int k=2; k <= segmentCount-2; ++k)
1848  integrals[k] = detail::singleSpline3ConvolvePolygon(augmentedArcLength, augmentedPoly,
1849  splineIndices[k-2], splineIndices[k], splineIndices[k+2]);
1850 
1851  BSpline<7, double> spline7;
1852  if(isOpenPolygon)
1853  {
1854  int solutionSize = segmentCount + 1;
1855  Matrix<double> m(solutionSize, solutionSize),
1856  rhs(solutionSize, 2),
1857  solution(solutionSize, 2);
1858  for(int k=0; k<solutionSize; ++k)
1859  {
1860  for(int l=-3; l<=3; ++l)
1861  {
1862  if(k + l < 0)
1863  {
1864  m(k, 0) += 2.0*spline7(l);
1865  m(k, abs(k+l)) -= spline7(l);
1866  }
1867  else if(k + l >= solutionSize)
1868  {
1869  m(k, solutionSize - 1) += 2.0*spline7(l);
1870  m(k, 2*solutionSize - k - l - 2) -= spline7(l);
1871  }
1872  else
1873  m(k,k+l) += spline7(l);
1874  }
1875  rhs(k, 0) = integrals[k][0];
1876  rhs(k, 1) = integrals[k][1];
1877  }
1878 
1879  linearSolve(m, rhs, solution);
1880 
1881  for(int k=0; k<solutionSize; ++k)
1882  {
1883  splinePoints.push_back(Point(solution(k,0), solution(k,1)));
1884  }
1885  splinePoints.push_back(2.0*splinePoints[solutionSize-1] - splinePoints[solutionSize-2]);
1886  splinePoints.insert(splinePoints.begin(), 2.0*splinePoints[0] - splinePoints[1]);
1887  }
1888  else
1889  {
1890  int solutionSize = segmentCount;
1891  Matrix<double> m(solutionSize, solutionSize),
1892  rhs(solutionSize, 2),
1893  solution(solutionSize, 2);
1894  for(int k=0; k<solutionSize; ++k)
1895  {
1896  for(int l=-3; l<=3; ++l)
1897  m(k, (k+l+solutionSize) % solutionSize) = spline7(l);
1898  rhs(k, 0) = integrals[k][0];
1899  rhs(k, 1) = integrals[k][1];
1900  }
1901  linearSolve(m, rhs, solution);
1902 
1903  for(int k=0; k<solutionSize; ++k)
1904  {
1905  splinePoints.push_back(Point(solution(k,0), solution(k,1)));
1906  }
1907  splinePoints.push_back(splinePoints[0]);
1908  }
1909 }
1910 
1911 #endif
1912 
1913 //@}
1914 
1915 } // namespace vigra
1916 
1917 namespace std {
1918 
1919 template <class T>
1920 ostream & operator<<(ostream & s, vigra::Polygon<T> const & a)
1921 {
1922  for(std::size_t k=0; k<a.size()-1; ++k)
1923  s << a[k] << ", ";
1924  if(a.size())
1925  s << a.back();
1926  return s;
1927 }
1928 
1929 } // namespace std
1930 
1931 #endif /* VIGRA_POLYGON_HXX */
reference back()
Definition: array_vector.hxx:321
PromoteTraits< V1, V2 >::Promote dot(RGBValue< V1, RIDX1, GIDX1, BIDX1 > const &r1, RGBValue< V2, RIDX2, GIDX2, BIDX2 > const &r2)
dot product
Definition: rgbvalue.hxx:906
void transpose(const MultiArrayView< 2, T, C1 > &v, MultiArrayView< 2, T, C2 > &r)
Definition: matrix.hxx:965
Int32 roundi(FixedPoint16< IntBits, OverflowHandling > v)
rounding to the nearest integer.
Definition: fixedpoint.hxx:1775
Diff2D operator-(Diff2D const &a, Diff2D const &b)
Definition: diff2d.hxx:711
double arcLengthQuantile(double quantile) const
Definition: polygon.hxx:379
void extractContour(MultiArrayView< 2, T, S > const &label_image, Shape2 const &anchor_point, PointArray &contour_points)
Create a polygon from the interpixel contour of a labeled region.
Definition: polygon.hxx:766
const difference_type & shape() const
Definition: multi_array.hxx:1648
bool empty() const
Definition: array_vector.hxx:351
linalg::TemporaryMatrix< T > exp(MultiArrayView< 2, T, C > const &v)
const_iterator begin() const
Definition: array_vector.hxx:223
FFTWComplex< R >::SquaredNormType squaredNorm(const FFTWComplex< R > &a)
squared norm (= squared magnitude)
Definition: fftw3.hxx:1044
int round(FixedPoint< IntBits, FracBits > v)
rounding to the nearest integer.
Definition: fixedpoint.hxx:683
reverse_iterator rend()
Definition: array_vector.hxx:279
Diff2D operator+(Diff2D const &a, Diff2D const &b)
Definition: diff2d.hxx:739
const_reverse_iterator crend() const
Definition: array_vector.hxx:300
Point interpolate(unsigned int index, double offset) const
Definition: polygon.hxx:187
std::ptrdiff_t MultiArrayIndex
Definition: multi_fwd.hxx:60
const_reverse_iterator crbegin() const
Definition: array_vector.hxx:293
Definition: polygon.hxx:78
FFTWComplex< R >::NormType norm(const FFTWComplex< R > &a)
norm (= magnitude)
Definition: fftw3.hxx:1037
reference operator[](difference_type i)
Definition: array_vector.hxx:335
Definition: array_vector.hxx:58
NumericTraits< T >::Promote sq(T t)
The square function.
Definition: mathutil.hxx:382
reference front()
Definition: array_vector.hxx:307
NumericTraits< V >::Promote sum(TinyVectorBase< V, SIZE, D1, D2 > const &l)
sum of the vector's elements
Definition: tinyvector.hxx:2073
bool isInside(difference_type const &p) const
Definition: multi_array.hxx:1717
bool contains(const_reference point, coordinate_type tolerance) const
Definition: polygon.hxx:524
bool closeAtTolerance(T1 l, T2 r, typename PromoteTraits< T1, T2 >::Promote epsilon)
Tolerance based floating-point equality.
Definition: mathutil.hxx:1638
const_iterator cend() const
Definition: array_vector.hxx:258
const_iterator cbegin() const
Definition: array_vector.hxx:251
FFTWComplex< R >::NormType abs(const FFTWComplex< R > &a)
absolute value (= magnitude)
Definition: fftw3.hxx:1002
Base class for, and view to, vigra::MultiArray.
Definition: multi_array.hxx:704
reverse_iterator rbegin()
Definition: array_vector.hxx:265
const_iterator end() const
Definition: array_vector.hxx:237
size_type size() const
Definition: array_vector.hxx:358
void convexHull(const PointArray1 &points, PointArray2 &convex_hull)
Compute convex hull of a 2D polygon.
Definition: polygon.hxx:838
int ceil(FixedPoint< IntBits, FracBits > v)
rounding up.
Definition: fixedpoint.hxx:675
void fillPolygon(Polygon< Point > const &p, MultiArrayView< 2, T, S > &output_image, Value value)
Render closed polygon p into the image output_image.
Definition: polygon.hxx:1005
int floor(FixedPoint< IntBits, FracBits > v)
rounding down.
Definition: fixedpoint.hxx:667
bool closed() const
Returns true iff the last and first points are equal or the polygon is empty.
Definition: polygon.hxx:177
bool linearSolve(...)
SquareRootTraits< FixedPoint< IntBits, FracBits > >::SquareRootResult sqrt(FixedPoint< IntBits, FracBits > v)
square root.
Definition: fixedpoint.hxx:616

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
vigra 1.11.1 (Fri May 19 2017)