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

131 lines
5.0 KiB
Python

import numpy
import sys
from shaperrec import manipulation
# *************************************************************
# 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
##**************************************
##
class SegmentExtender:
"""Extend Segments part of a list of Path by aggregating points from neighbouring Path objects.
There are 2 concrete subclasses for extending forward and backward (due to technical reasons).
"""
def __init__(self, relD, fitQ):
self.relD = relD
self.fitQ = fitQ
def nextPaths(self, seg):
pL = []
p = self.getNext(seg) # prev or next
while p :
if p.isSegment(): break
if p.mergedObj is None: break
pL.append(p)
p = self.getNext(p)
if pL==[]:
return []
return pL
def extend(self, seg):
nextPathL = self.nextPaths(seg)
debug('extend ', self.extDir, seg, nextPathL, seg.length, len(nextPathL))
if nextPathL==[]: return seg
pointsToTest = numpy.concatenate( [p.points for p in nextPathL] )
mergeD = seg.length*self.relD
#print seg.point1 , seg.pointN, pointsToTest
pointsToFit, addedPoints = self.pointsToFit(seg, pointsToTest, mergeD)
if len(pointsToFit)==0:
return seg
newseg = manipulation.fitSingleSegment(pointsToFit)
if newseg.quality()>self.fitQ: # fit failed
return seg
debug( ' EXTENDING ! ', len(seg.points), len(addedPoints) )
self.removePath(seg, newseg, nextPathL, addedPoints )
newseg.points = pointsToFit
seg.mergedObj= newseg
newseg.sourcepoints = seg.sourcepoints
return newseg
@staticmethod
def extendSegments(segmentList, relD=0.03, qual=0.5):
"""Perform Segment extension from list of Path segmentList
returns the updated list of Path objects"""
fwdExt = FwdExtender(relD, qual)
bwdExt = BwdExtender(relD, qual)
# tag all objects with an attribute pointing to the extended object
for seg in segmentList:
seg.mergedObj = seg # by default the extended object is self
# extend each segments, starting by the longest
for seg in sorted(segmentList, key = lambda s : s.length, reverse=True):
if seg.isSegment():
newseg=fwdExt.extend(seg)
seg.mergedObj = bwdExt.extend(newseg)
# the extension procedure has marked as None the mergedObj
# which have been swallowed by an extension.
# filter them out :
updatedSegs=[seg.mergedObj for seg in segmentList if seg.mergedObj]
return updatedSegs
class FwdExtender(SegmentExtender):
extDir='Fwd'
def getNext(self, seg):
return seg.__next__ if hasattr(seg, "__next__") else None
def pointsToFit(self, seg, pointsToTest, mergeD):
distancesToLine =abs(seg.a*pointsToTest[:, 0]+seg.b*pointsToTest[:, 1]+seg.c)
goodInd=len(pointsToTest)
for i, d in reversed(list(enumerate(distancesToLine))):
if d<mergeD: goodInd=i;break
addedPoints = pointsToTest[:len(pointsToTest-goodInd)]
#debug( ' ++ pointsToFit ' , mergeD, i ,len(pointsToTest), addedPoints , seg.points )
return numpy.concatenate([seg.points, addedPoints]), addedPoints
def removePath(self, seg, newseg, nextPathL, addedPoints):
npoints = len(addedPoints)
acc=0
newseg.prev = seg.prev
for p in nextPathL:
if (acc+len(p.points))<=npoints:
p.mergedObj = None
acc += len(p.points)
else:
newseg.next = p
p.points = p.points[:(npoints-acc-len(p.points))]
break
class BwdExtender(SegmentExtender):
extDir='Bwd'
def getNext(self, seg):
return seg.prev
def pointsToFit(self, seg, pointsToTest, mergeD):
# TODO: shouldn't the distances be sorted cclosest to furthest
distancesToLine =abs(seg.a*pointsToTest[:, 0]+seg.b*pointsToTest[:, 1]+seg.c)
goodInd=len(pointsToTest)
for i, d in enumerate(distancesToLine):
if d<mergeD: goodInd=i; break
addedPoints = pointsToTest[goodInd:]
#debug( ' ++ pointsToFit ' , mergeD, i ,len(pointsToTest), addedPoints , seg.points )
return numpy.concatenate([addedPoints, seg.points]), addedPoints
def removePath(self, seg, newseg, nextPathL, addedPoints):
npoints = len(addedPoints)
acc=0
newseg.next = seg.__next__ if hasattr(seg, "__next__") else None
for p in reversed(nextPathL):
if (acc+len(p.points))<=npoints:
p.mergedObj = None
acc += len(p.points)
else:
newseg.prev = p
p.points = p.points[(npoints-acc-len(p.points)):]
break