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

polygon.hxx
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
51namespace vigra {
52
53namespace detail {
54
55template < class Point >
56bool pointYXOrdering(Point const & p1, Point const & p2)
57{
58 return (p1[1]<p2[1]) || (p1[1] == p2[1] && p1[0] < p2[0]);
59}
60
61template < class Point >
62bool 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 */
77template<class POINT=TinyVector<double, 2> >
79: protected ArrayVector<POINT>
80{
81 public:
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
478template <class POINT>
479POINT 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
522template <class POINT>
523bool
524Polygon<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
622template <class POINT>
623inline 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
633template <class POINT>
634inline 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
644template <class POINT>
645inline Polygon<POINT>
646operator+(Polygon<POINT> const & p, POINT const & offset)
647{
648 return Polygon<POINT>(p) += offset;
649}
650
651template <class POINT>
652inline Polygon<POINT>
653operator+(POINT const & offset, Polygon<POINT> const & p)
654{
655 return Polygon<POINT>(p) += offset;
656}
657
658template <class POINT>
659inline Polygon<POINT>
660operator-(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
668template <class POINT>
669inline Polygon<POINT>
670operator-(Polygon<POINT> const & p, POINT const & offset)
671{
672 return Polygon<POINT>(p) -= offset;
673}
674
675template <class POINT>
676inline Polygon<POINT>
677operator*(Polygon<POINT> const & p, double scale)
678{
679 return Polygon<POINT>(p) *= scale;
680}
681
682template <class POINT>
683inline Polygon<POINT>
684operator*(double scale, Polygon<POINT> const & p)
685{
686 return Polygon<POINT>(p) *= scale;
687}
688
689
690template <class POINT>
691inline Polygon<POINT>
692operator/(Polygon<POINT> const & p, double scale)
693{
694 return Polygon<POINT>(p) /= scale;
695}
696
697template <class POINT>
698inline Polygon<POINT>
699transpose(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
709template <class POINT>
710inline Polygon<POINT>
711reverse(Polygon<POINT> const & p)
712{
713 Polygon<POINT> result(p);
714 result.reverse();
715 return result;
716}
717
718template<class Point>
719Point 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
739namespace std {
740
741template<class T>
742void swap(vigra::Polygon<T> &a, vigra::Polygon<T> &b)
743{
744 a.swap(b);
745}
746
747} // namespace std
748
749/********************************************************************/
750
751namespace 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*/
764template<class T, class S, class PointArray>
765void
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*/
837template<class PointArray1, class PointArray2>
838void 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
891namespace 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 */
899template<class Point, class Array>
900void 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
973template<class Point, class FUNCTOR>
974bool
975inspectPolygon(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 */
1004template<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
1043template<bool useMaxStep, class PointIterator, class TargetArray>
1044void 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
1126template<class PointArray>
1127void 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
1135template<class PointArray>
1136void 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
1147namespace detail {
1148
1149template <class Point>
1150Point 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
1158template<class PointArray>
1159void 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
1248template<class PointArray>
1249void 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
1378template<class PointArray>
1379void 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
1413template<class PointArray>
1414void 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
1522namespace detail {
1523
1524template<class ArcLengthList, class PointList>
1525typename PointList::value_type
1526singleGaussianConvolvePolygonReflective(
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
1584template<class ArcLengthList, class PointList>
1585typename PointList::value_type
1586singleGaussianConvolvePolygonCyclic(
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
1636template<class PointArray>
1637void 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
1682namespace detail {
1683
1684template<class Point>
1685Point 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
1703template<class ArcLengthList, class PointList>
1704typename PointList::value_type
1705singleSpline3ConvolvePolygon(
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
1732template<class PointArray>
1733void 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
1917namespace std {
1918
1919template <class T>
1920ostream & 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 */
const_iterator begin() const
Definition array_vector.hxx:223
const_iterator cbegin() const
Definition array_vector.hxx:251
size_type size() const
Definition array_vector.hxx:358
bool empty() const
Definition array_vector.hxx:351
reverse_iterator rend()
Definition array_vector.hxx:279
reference front()
Definition array_vector.hxx:307
const_reverse_iterator crbegin() const
Definition array_vector.hxx:293
const_iterator cend() const
Definition array_vector.hxx:258
reference operator[](difference_type i)
Definition array_vector.hxx:335
const_reverse_iterator crend() const
Definition array_vector.hxx:300
const_iterator end() const
Definition array_vector.hxx:237
reverse_iterator rbegin()
Definition array_vector.hxx:265
reference back()
Definition array_vector.hxx:321
Definition array_vector.hxx:514
Base class for, and view to, vigra::MultiArray.
Definition multi_fwd.hxx:127
bool isInside(difference_type const &p) const
Definition multi_array.hxx:1719
const difference_type & shape() const
Definition multi_array.hxx:1650
Definition polygon.hxx:80
bool contains(const_reference point, coordinate_type tolerance) const
Definition polygon.hxx:524
double arcLengthQuantile(double quantile) const
Definition polygon.hxx:379
Point interpolate(unsigned int index, double offset) const
Definition polygon.hxx:187
bool closed() const
Returns true iff the last and first points are equal or the polygon is empty.
Definition polygon.hxx:177
Class for fixed size vectors.
Definition tinyvector.hxx:1008
int floor(FixedPoint< IntBits, FracBits > v)
rounding down.
Definition fixedpoint.hxx:667
int round(FixedPoint< IntBits, FracBits > v)
rounding to the nearest integer.
Definition fixedpoint.hxx:683
Diff2D operator+(Diff2D const &a, Diff2D const &b)
Definition diff2d.hxx:725
FFTWComplex< R >::NormType norm(const FFTWComplex< R > &a)
norm (= magnitude)
Definition fftw3.hxx:1037
void convexHull(const PointArray1 &points, PointArray2 &convex_hull)
Compute convex hull of a 2D polygon.
Definition polygon.hxx:838
NumericTraits< T >::Promote sq(T t)
The square function.
Definition mathutil.hxx:382
Int32 roundi(FixedPoint16< IntBits, OverflowHandling > v)
rounding to the nearest integer.
Definition fixedpoint.hxx:1775
NumericTraits< V >::Promote sum(TinyVectorBase< V, SIZE, D1, D2 > const &l)
sum of the vector's elements
Definition tinyvector.hxx:2073
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
FFTWComplex< R >::NormType abs(const FFTWComplex< R > &a)
absolute value (= magnitude)
Definition fftw3.hxx:1002
FFTWComplex< R >::SquaredNormType squaredNorm(const FFTWComplex< R > &a)
squared norm (= squared magnitude)
Definition fftw3.hxx:1044
bool closeAtTolerance(T1 l, T2 r, typename PromoteTraits< T1, T2 >::Promote epsilon)
Tolerance based floating-point equality.
Definition mathutil.hxx:1638
std::ptrdiff_t MultiArrayIndex
Definition multi_fwd.hxx:60
int ceil(FixedPoint< IntBits, FracBits > v)
rounding up.
Definition fixedpoint.hxx:675
Diff2D operator-(Diff2D const &a, Diff2D const &b)
Definition diff2d.hxx:697
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
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

© 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.12.2