188 lines
5.2 KiB
Python
188 lines
5.2 KiB
Python
|
import numpy
|
||
|
import sys
|
||
|
from shaperrec import groups
|
||
|
from shaperrec import geometric
|
||
|
from shaperrec import internal
|
||
|
|
||
|
# *************************************************************
|
||
|
# debugging
|
||
|
def void(*l):
|
||
|
pass
|
||
|
def debug_on(*l):
|
||
|
sys.stderr.write(' '.join(str(i) for i in l) +'\n')
|
||
|
debug = void
|
||
|
#debug = debug_on
|
||
|
|
||
|
# *************************************************************
|
||
|
# Object manipulation functions
|
||
|
|
||
|
def toRemarkableShape( group ):
|
||
|
"""Test if PathGroup instance 'group' looks like a remarkable shape (ex: Rectangle).
|
||
|
if so returns a new shape instance else returns group unchanged"""
|
||
|
r = groups.Rectangle.isRectangle( group )
|
||
|
if r : return r
|
||
|
return group
|
||
|
|
||
|
|
||
|
def resetPrevNextSegment(segs):
|
||
|
for i, seg in enumerate(segs[:-1]):
|
||
|
s = segs[i+1]
|
||
|
seg.next = s
|
||
|
s.prev = seg
|
||
|
return segs
|
||
|
|
||
|
|
||
|
def fitSingleSegment(a):
|
||
|
xmin, ymin, w, h = geometric.computeBox(a)
|
||
|
inverse = w<h
|
||
|
if inverse:
|
||
|
a = numpy.roll(a, 1, axis=1)
|
||
|
|
||
|
seg = regLin(a)
|
||
|
if inverse:
|
||
|
seg.inverse()
|
||
|
#a = numpy.roll(a,1,axis=0)
|
||
|
return seg
|
||
|
|
||
|
def regLin(a , returnOnlyPars=False):
|
||
|
"""perform a linear regression on 2dim array a. Creates a segment object in return """
|
||
|
sumX = a[:, 0].sum()
|
||
|
sumY = a[:, 1].sum()
|
||
|
sumXY = (a[:, 1]*a[:, 0]).sum()
|
||
|
a2 = a*a
|
||
|
sumX2 = a2[:, 0].sum()
|
||
|
sumY2 = a2[:, 1].sum()
|
||
|
N = a.shape[0]
|
||
|
|
||
|
pa = (N*sumXY - sumX*sumY)/ ( N*sumX2 - sumX*sumX)
|
||
|
pb = (sumY - pa*sumX) /N
|
||
|
if returnOnlyPars:
|
||
|
return pa, -1, pb
|
||
|
return internal.Segment(pa, -1, pb, a)
|
||
|
|
||
|
|
||
|
def smoothArray(a, n=2):
|
||
|
count = numpy.zeros(a.shape)
|
||
|
smootha = numpy.array(a)
|
||
|
for i in range(n):
|
||
|
count[i]=n+i+1
|
||
|
count[-i-1] = n+i+1
|
||
|
count[n:-n] = n+n+1
|
||
|
#debug('smooth ', len(smooth[:-2]) [)
|
||
|
for i in range(1, n+1):
|
||
|
smootha[:-i] += a[i:]
|
||
|
smootha[i:] += a[:-i]
|
||
|
return smootha/count
|
||
|
|
||
|
def buildTangents( points , averaged=True, isClosing=False):
|
||
|
"""build tangent vectors to the curve 'points'.
|
||
|
if averaged==True, the tangents are averaged with their direct neighbours (use case : smoother tangents)"""
|
||
|
tangents = numpy.zeros( (len(points), 2) )
|
||
|
i=1
|
||
|
tangents[:-i] += points[i:] - points[:-i] # i <- p_i+1 - p_i
|
||
|
tangents[i:] += points[i:] - points[:-i] # i <- p_i - p_i-1
|
||
|
if isClosing:
|
||
|
tangents[0] += tangents[0] - tangents[-1]
|
||
|
tangents[-1] += tangents[0] - tangents[-1]
|
||
|
tangents *= 0.5
|
||
|
if not isClosing:
|
||
|
tangents[0] *=2
|
||
|
tangents[-1] *=2
|
||
|
|
||
|
|
||
|
## debug('points ', points)
|
||
|
## debug('buildTangents --> ', tangents )
|
||
|
|
||
|
if averaged:
|
||
|
# average over neighbours
|
||
|
avTan = numpy.array(tangents)
|
||
|
avTan[:-1] += tangents[1:]
|
||
|
avTan[1:] += tangents[:-1]
|
||
|
if isClosing:
|
||
|
tangents[0]+=tangents[-1]
|
||
|
tangents[1]+=tangents[0]
|
||
|
avTan *= 1./3
|
||
|
if not isClosing:
|
||
|
avTan[0] *=1.5
|
||
|
avTan[-1] *=1.5
|
||
|
|
||
|
return avTan
|
||
|
|
||
|
|
||
|
def clusterAngles(array, dAng=0.15):
|
||
|
"""Cluster together consecutive angles with similar values (within 'dAng').
|
||
|
array : flat array of angles
|
||
|
returns [ ..., (indi_0, indi_1),...] where each tuple are indices of cluster i
|
||
|
"""
|
||
|
N = len(array)
|
||
|
|
||
|
closebyAng = numpy.zeros( (N, 4), dtype=int)
|
||
|
|
||
|
for i, a in enumerate(array):
|
||
|
cb = closebyAng[i]
|
||
|
cb[0] =i
|
||
|
cb[2]=i
|
||
|
cb[3]=i
|
||
|
c=i-1
|
||
|
# find number of angles within dAng in nearby positions
|
||
|
while c>-1: # indices below i
|
||
|
d=geometric.closeAngleAbs(a, array[c])
|
||
|
if d>dAng:
|
||
|
break
|
||
|
cb[1]+=1
|
||
|
cb[2]=c
|
||
|
c-=1
|
||
|
c=i+1
|
||
|
while c<N-1:# indices above i
|
||
|
d=geometric.closeAngleAbs(a, array[c])
|
||
|
if d>dAng:
|
||
|
break
|
||
|
cb[1]+=1
|
||
|
cb[3]=c
|
||
|
c+=1
|
||
|
closebyAng= closebyAng[numpy.argsort(closebyAng[:, 1]) ]
|
||
|
|
||
|
clusteredPos = numpy.zeros(N, dtype=int)
|
||
|
clusters = []
|
||
|
for cb in reversed(closebyAng):
|
||
|
if clusteredPos[cb[0]]==1:
|
||
|
continue
|
||
|
# try to build a cluster
|
||
|
minI = cb[2]
|
||
|
while clusteredPos[minI]==1:
|
||
|
minI+=1
|
||
|
maxI = cb[3]
|
||
|
while clusteredPos[maxI]==1:
|
||
|
maxI-=1
|
||
|
for i in range(minI, maxI+1):
|
||
|
clusteredPos[i] = 1
|
||
|
clusters.append( (minI, maxI) )
|
||
|
|
||
|
return clusters
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def adjustAllAngles(paths):
|
||
|
for p in paths:
|
||
|
if p.isSegment() and p.newAngle is not None:
|
||
|
p.adjustToNewAngle()
|
||
|
# next translate to fit end points
|
||
|
tr = numpy.zeros(2)
|
||
|
for p in paths[1:]:
|
||
|
if p.isSegment() and p.prev.isSegment():
|
||
|
tr = p.prev.pointN - p.point1
|
||
|
debug(' translating ', p, ' prev is', p.prev, ' ', tr, )
|
||
|
p.translate(tr)
|
||
|
|
||
|
def adjustAllDistances(paths):
|
||
|
for p in paths:
|
||
|
if p.isSegment() and p.newLength is not None:
|
||
|
p.adjustToNewDistance()
|
||
|
# next translate to fit end points
|
||
|
tr = numpy.zeros(2)
|
||
|
for p in paths[1:]:
|
||
|
if p.isSegment() and p.prev.isSegment():
|
||
|
tr = p.prev.pointN - p.point1
|
||
|
p.translate(tr)
|