mightyscape-1.2/extensions/fablabchemnitz/shape_recognition/shaperrec/manipulation.py

188 lines
5.2 KiB
Python
Raw Normal View History

2022-10-13 00:05:56 +02:00
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)