'''
general functions for polylines =
[ [ [x0,y0],[x1,y1], ... ], #first polyline
[ [x0,y0],[x1,y1], ... ], #second
...
]
'''
import numpy as np
from numpy.linalg import norm
from scipy.interpolate import UnivariateSpline
from scipy.stats import linregress
from fancytools.math import line
from fancytools.math.angleDiff import angleDiff
[docs]def lengths(polyline):
'''
returns the length of all polylines
'''
return norm(np.diff(polyline, axis=0), axis=1)
def _sort(c, firstI, lastI):
# sort given polyline giving
# indices of first and last point
l = len(c)
indices = np.zeros(l, dtype=np.uint16)
remaining = np.ones(l, dtype=bool)
remaining[firstI] = False
remaining[lastI] = False
indices[0] = firstI
indices[-1] = lastI
nextI = firstI
for m in xrange(1, l-1):
p = c[nextI]
min_dist = 1e6
for j, other in enumerate(c):
if remaining[j]:
dist = norm(p[0]-other[0])
if dist < min_dist:
nextI = j
min_dist = dist
indices[m] = nextI
remaining[nextI] = False
return indices
[docs]def sort(polylines):
'''
sort points within polyline
p0-p1-p2...
'''
for n, c in enumerate(polylines):
l = len(c)
if l > 2:
# DEFINE FIRST AND LAST INDEX A THOSE TWO POINTS THAT
# HAVE THE BIGGEST DIFFERENCE FROM A MIDDLE:
mid = c.mean(axis=0)
distV = (c-mid)
dists = norm(distV, axis=-1)
firstI = np.argmax(dists)
sign = np.sign(distV[firstI])
dd = np.logical_or(np.sign(distV[:,0]) != sign[0],
np.sign(distV[:, 1]) != sign[1] )
dists[~dd] = 0
lastI = np.argmax(dists)
ind = _sort(c, firstI, lastI)
c = c[ind]
polylines[n] = c
[docs]def filter(polylines, min_len=20):
'''
filter polylines shorter than given min length
'''
filtered = []
for n in xrange(len(polylines)-1,-1,-1):
if lengths(polylines[n]).sum() < min_len:
filtered.append(polylines.pop(n))
return filtered
[docs]def separate(polylines, f_mx_dist=2, mn_group_len=4):
'''
split polylines wherever crinkles are found
'''
s = []
for n in xrange(len(polylines)-1, -1, -1):
c = polylines[n]
separated = False
start = 0
for m in xrange(mn_group_len, len(c)-1):
if m-start < mn_group_len:
continue
m+=1
group = c[m-mn_group_len:m]
x,y = group[:,0], group[:,1]
asc, offs, _, _, _ = linregress(x,y)
yfit = asc*x+offs
#check whether next point would fit in:
p1 = c[m]
l = (x[0], yfit[0], p1[-1], asc*p1[-1]+offs)
std = np.mean([line.distance(l,g) for g in group])
dist = line.distance(l, p1)
if dist > 2 and dist > f_mx_dist*std:
separated = True
s.append(c[start:m-1])
start = m-1
if separated:
if len(c)-start >= 2:
s.append(c[start:])
polylines.pop(n)
polylines.extend(s)
return polylines
[docs]def merge(polylines, mx_dist=4):
'''
point by line segment comparison
merge polylines if points are close
'''
l = len(polylines)
to_remove = set()
for n in xrange(l-1, -1, -1):
if n not in to_remove:
c = polylines[n]
for p0, p1 in zip(c[:-1], c[1:]):
#create a line from any subsegment:
l0 = p0[0], p0[1], p1[0], p1[1]
#for every other polyline:
for m in xrange(l-1, -1, -1):
if m not in to_remove:
if n == m:
continue
remove = False
cc = polylines[m]
ind = np.zeros(shape=cc.shape[0], dtype=bool)
#for every point p in this polyline:
for o in xrange(len(cc)-1, -1, -1):
p = cc[o]
if line.segmentDistance(l0, p) < mx_dist:
remove = True
ind[o] = True
if remove:
polylines[n] = np.append(c, cc[ind], axis=0)
ind= ~ind
s = ind.sum()
if s < 2:
to_remove.add(m)
else:
polylines[m] = cc[ind]
to_remove = list(to_remove)
to_remove.sort()
to_remove.reverse()
for i in to_remove:
polylines.pop(i)
#TODO: get direction d0,d1 by meas dists
def _connect(c0,c1, d0,d1):
#connect both polylines is they have a similar orientation
#d0, d1: which position if connected
#0-> start pos
#1-> end position
#sort positions as follows:
# c0 c1
#p0---p1 p2--p3
if d0 == 0:
p0 = c0[-1]
p1 = c0[0]
else:
p0 = c0[0]
p1 = c0[-1]
if d1 == 0:
p2 = c1[0]
p3 = c1[-1]
else:
p2 = c1[-1]
p3 = c1[0]
a0 = line.angle((p0[0],p0[1],p1[0],p1[1]))
a1 = line.angle((p2[0],p2[1],p3[0],p3[1]))
diff = angleDiff(a0,a1)
is_same = False
#CRITERION 1: similar angle
if abs(diff) < 0.3:
#CRITERION 2: all points in a row
if norm(p0-p2)<norm(p0-p3):
is_same = True
#create connected polyline:
if d0==0:
if d1==0:
c0 = np.insert(c0,0,c1[::-1],axis=0)
else:
c0 = np.insert(c0,0,c1,axis=0)
else:
if d1==0:
c0 = np.append(c0,c1,axis=0)
else:
c0 = np.append(c0,c1[::-1],axis=0)
return is_same, c0
[docs]def connect(polylines, max_dist=10):
'''
connect polylines that are close and have a similar orientation
o---o <-> o---o ==> o----o--o----o
TODO: max_dist as faction of cell size
'''
ll = len(polylines)
remove = []
for n in xrange(ll-1,-1,-1):
c = polylines[n]
if len(c)>1:
for d0,p0 in enumerate((c[0,0],c[-1,0])):
for m in xrange(len(polylines)-1,-1,-1):
#combine all x all polylines
if n==m:
continue
cc = polylines[m]
for d1,p1 in enumerate((cc[0,0],cc[-1,0])):
# for end points of other polylines:
# measure closest distance for current polyline
ndist = norm(p0-p1)
if ndist < max_dist:
is_same, c = _connect(c,cc, d0,d1)
if is_same:
if m not in remove:
remove.append(m)
polylines[n] = c
break
# remove those which are already in connected to other polyline:
remove.sort()
remove.reverse()
for r in remove:
polylines.pop(r)
[docs]def smooth(polylines):
'''
smooth every polyline using spline interpolation
'''
for c in polylines:
if len(c)<9:
# smoothing wouldn't make sense here
continue
x = c[:, 0]
y = c[:, 1]
t = np.arange(x.shape[0], dtype=float)
t /= t[-1]
x = UnivariateSpline(t, x)(t)
y = UnivariateSpline(t, y)(t)
c[:, 0] = x
c[:, 1] = y
[docs]def plot(polylines, img=None):
import pylab as plt
plt.figure(1)
for n, c in enumerate(polylines):
x = c[:, 0]
y = c[:, 1]
plt.plot(x, y, linewidth=3)
plt.text(x[-1], y[-1], str(n+1))
if not img is None:
plt.imshow(img, interpolation='none')
plt.set_cmap('gray')
plt.show()
if __name__ == '__main__':
#TODO
pass