ConcaveHull.py
5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/bin/env python
##
## calculate the concave hull of a set of points
## see: CONCAVE HULL: A K-NEAREST NEIGHBOURS APPROACH
## FOR THE COMPUTATION OF THE REGION OCCUPIED BY A
## SET OF POINTS
## Adriano Moreira and Maribel Yasmina Santos 2007
##
import numpy as np
import scipy.spatial as spt
import matplotlib.pyplot as plt
from matplotlib.path import Path
from test.zonghe import lineintersect as li
def GetFirstPoint(dataset):
''' Returns index of first point, which has the lowest y value '''
# todo: what if there is more than one point with lowest y?
imin = np.argmin(dataset[:,1])
return dataset[imin]
def GetNearestNeighbors(dataset, point, k):
''' Returns indices of k nearest neighbors of point in dataset'''
# todo: experiment with leafsize for performance
mytree = spt.cKDTree(dataset,leafsize=10)
distances, indices = mytree.query(point,k)
# todo: something strange here, we get more indices than points in dataset
# so have to do this
return dataset[indices[:dataset.shape[0]]]
def SortByAngle(kNearestPoints, currentPoint, prevPoint):
''' Sorts the k nearest points given by angle '''
angles = np.zeros(kNearestPoints.shape[0])
i = 0
for NearestPoint in kNearestPoints:
# calculate the angle
angle = np.arctan2(NearestPoint[1]-currentPoint[1],
NearestPoint[0]-currentPoint[0]) - \
np.arctan2(prevPoint[1]-currentPoint[1],
prevPoint[0]-currentPoint[0])
angle = np.rad2deg(angle)
# only positive angles
angle = np.mod(angle+360,360)
#print NearestPoint[0], NearestPoint[1], angle
angles[i] = angle
i=i+1
return kNearestPoints[np.argsort(angles)]
# def plotPoints(dataset):
# plt.plot(dataset[:,0],dataset[:,1],'o',markersize=10,markerfacecolor='0.75',
# markeredgewidth=1)
# plt.axis('equal')
# plt.axis([min(dataset[:,0])-0.5,max(dataset[:,0])+0.5,min(dataset[:,1])-0.5,
# max(dataset[:,1])+0.5])
# plt.show()
#
# def plotPath(dataset, path):
# plt.plot(dataset[:,0],dataset[:,1],'o',markersize=10,markerfacecolor='0.65',
# markeredgewidth=0)
# path = np.asarray(path)
# plt.plot(path[:,0],path[:,1],'o',markersize=10,markerfacecolor='0.55',
# markeredgewidth=0)
# plt.plot(path[:,0],path[:,1],'-',lw=1.4,color='k')
# plt.axis('equal')
# plt.axis([min(dataset[:,0])-0.5,max(dataset[:,0])+0.5,min(dataset[:,1])-0.5,
# max(dataset[:,1])+0.5])
# plt.axis('off')
# plt.savefig('./doc/figure_1.png', bbox_inches='tight')
# plt.show()
def removePoint(dataset, point):
delmask = [np.logical_or(dataset[:,0]!=point[0],dataset[:,1]!=point[1])]
newdata = dataset[delmask]
return newdata
def concaveHull(dataset, k):
assert k >= 3, 'k has to be greater or equal to 3.'
points = dataset
# todo: remove duplicate points from dataset
# todo: check if dataset consists of only 3 or less points
# todo: make sure that enough points for a given k can be found
firstpoint = GetFirstPoint(points)
# init hull as list to easily append stuff
hull = []
# add first point to hull
hull.append(firstpoint)
# and remove it from dataset
points = removePoint(points,firstpoint)
currentPoint = firstpoint
# set prevPoint to a Point righ of currentpoint (angle=0)
prevPoint = (currentPoint[0]+10, currentPoint[1])
step = 2
while ( (not np.array_equal(firstpoint, currentPoint) or (step==2)) and points.size > 0 ):
if ( step == 5 ): # we're far enough to close too early
points = np.append(points, [firstpoint], axis=0)
kNearestPoints = GetNearestNeighbors(points, currentPoint, k)
cPoints = SortByAngle(kNearestPoints, currentPoint, prevPoint)
# avoid intersections: select first candidate that does not intersect any
# polygon edge
its = True
i = 0
while ( (its==True) and (i<cPoints.shape[0]) ):
i=i+1
if ( np.array_equal(cPoints[i-1], firstpoint) ):
lastPoint = 1
else:
lastPoint = 0
j = 2
its = False
while ( (its==False) and (j<np.shape(hull)[0]-lastPoint) ):
its = li.doLinesIntersect(hull[step-1-1], cPoints[i-1],
hull[step-1-j-1],hull[step-j-1])
j=j+1
if ( its==True ):
print("all candidates intersect -- restarting with k = ",k+1)
return concaveHull(dataset,k+1)
prevPoint = currentPoint
currentPoint = cPoints[i-1]
# add current point to hull
hull.append(currentPoint)
points = removePoint(points,currentPoint)
step = step+1
# check if all points are inside the hull
p = Path(hull)
pContained = p.contains_points(dataset, radius=0.0000000001)
if (not pContained.all()):
print("not all points of dataset contained in hull -- restarting with k = ",k+1)
return concaveHull(dataset, k+1)
print("finished with k = ",k)
return hull
if __name__ == '__main__':
points_E = np.array([[1,1],[2,1],[3,1],[4,1],[5,1],[6,1],[6,2],[6,3],[5,3],[4,3],
[3,3],[3,4],[3,5],[4,5],[5,5],[5,6],[5,7],[4,7],[3,7],[3,8],
[3,9],[4,9],[5,9],[6,9],[6,10],[6,11],[5,11],[4,11],[3,11],[2,11],
[1,11],[1,10],[1,9],[1,8],[1,7],[1,6],[1,5],[1,4],[1,3],[1,2],
[5,2],[4,2],[3,2],[2,2],[2,3],[2,4],[2,5],[2,6],[2,7],[2,8],
[2,9],[2,10],[3,10],[4,10],[5,10],[3,6],[4,6],[5,6],[4.5,7],[3,8.5],
])
print(concaveHull(points_E, 3))