changeset 619:dc2d0a0d7fe1

merged code from Mohamed Gomaa Mohamed for the use of points of interests in mation pattern learning and motion prediction (TRB 2015)
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Wed, 10 Dec 2014 15:27:08 -0500
parents 04a8304e13f0 (diff) 1a92d28e2d05 (current diff)
children 582508610572
files python/events.py python/moving.py python/objectsmoothing.py python/poly-utils.py python/prediction.py python/storage.py
diffstat 11 files changed, 567 insertions(+), 380 deletions(-) [+]
line wrap: on
line diff
--- a/python/events.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/events.py	Wed Dec 10 15:27:08 2014 -0500
@@ -12,27 +12,27 @@
 __metaclass__ = type
 
 def findRoute(prototypes,objects,i,j,noiseEntryNums,noiseExitNums,minSimilarity= 0.3, spatialThreshold=1.0, delta=180):
-	if i[0] not in noiseEntryNums: 
-		prototypesRoutes= [ x for x in sorted(prototypes.keys()) if i[0]==x[0]]
-	elif i[1] not in noiseExitNums:
-		prototypesRoutes=[ x for x in sorted(prototypes.keys()) if i[1]==x[1]]
-	else:
-		prototypesRoutes=[x for x in sorted(prototypes.keys())]
-	routeSim={}
-	lcss = utils.LCSS(similarityFunc=lambda x,y: (distanceForLCSS(x,y) <= spatialThreshold),delta=delta)
-	for y in prototypesRoutes: 
-		if y in prototypes.keys():
-			prototypesIDs=prototypes[y]
-			similarity=[]
-			for x in prototypesIDs:
-				s=lcss.computeNormalized(objects[j].positions, objects[x].positions)
-				similarity.append(s)
-			routeSim[y]=max(similarity)
-	route=max(routeSim, key=routeSim.get)
-	if routeSim[route]>=minSimilarity:
-		return route
-	else:
-		return i
+    if i[0] not in noiseEntryNums: 
+        prototypesRoutes= [ x for x in sorted(prototypes.keys()) if i[0]==x[0]]
+    elif i[1] not in noiseExitNums:
+        prototypesRoutes=[ x for x in sorted(prototypes.keys()) if i[1]==x[1]]
+    else:
+        prototypesRoutes=[x for x in sorted(prototypes.keys())]
+    routeSim={}
+    lcss = utils.LCSS(similarityFunc=lambda x,y: (distanceForLCSS(x,y) <= spatialThreshold),delta=delta)
+    for y in prototypesRoutes: 
+        if y in prototypes.keys():
+            prototypesIDs=prototypes[y]
+            similarity=[]
+            for x in prototypesIDs:
+                s=lcss.computeNormalized(objects[j].positions, objects[x].positions)
+                similarity.append(s)
+            routeSim[y]=max(similarity)
+    route=max(routeSim, key=routeSim.get)
+    if routeSim[route]>=minSimilarity:
+        return route
+    else:
+        return i
 
 def getRoute(obj,prototypes,objects,noiseEntryNums,noiseExitNums,useDestination=True):
     route=(obj.startRouteID,obj.endRouteID)
@@ -177,9 +177,9 @@
             for i, cz in self.crossingZones.iteritems():
                 pPETs[i] = prediction.SafetyPoint.computeExpectedIndicator(cz)
             self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[9], pPETs))
-			
+
     def addVideoFilename(self,videoFilename):
-        self.videoFilename= videoFilename	
+        self.videoFilename= videoFilename
 
     def addInteractionType(self,interactionType):
         ''' interaction types: conflict or collision if they are known'''
--- a/python/moving.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/moving.py	Wed Dec 10 15:27:08 2014 -0500
@@ -726,6 +726,11 @@
             return Trajectory([[a-b for a,b in zip(self.getXCoordinates(),traj2.getXCoordinates())],
                                [a-b for a,b in zip(self.getYCoordinates(),traj2.getYCoordinates())]])
 
+    def multiply(self, alpha):
+        '''Returns a new trajectory of the same length'''
+        return Trajectory([[alpha*x for x in self.getXCoordinates()],
+                           [alpha*y for y in self.getYCoordinates()]])
+
     def differentiate(self, doubleLastPosition = False):
         diff = Trajectory()
         for i in xrange(1, self.length()):
@@ -1063,7 +1068,7 @@
         else:
             print('Load features to compute a maximum size')
             return None
-			
+    
     def setRoutes(self, startRouteID, endRouteID):
         self.startRouteID = startRouteID
         self.endRouteID = endRouteID
@@ -1286,17 +1291,127 @@
 ##################
 
 class BBAnnotation(MovingObject):
-    '''Class for : a series of ground truth annotations using bounding boxes
+    '''Class for a series of ground truth annotations using bounding boxes
     Its center is the center of the containing shape
+
+    By default in image space
     '''
 
-    def __init__(self, num = None, timeInterval = None, topPositions = None, bottomPositions = None, userType = userType2Num['unknown']):
-        super(BBAnnotation, self).__init__(num, timeInterval, Trajectory(), userType = userType)
-        self.topPositions = topPositions.getPositions()
-        self.bottomPositions = bottomPositions.getPositions()
-        for i in xrange(int(topPositions.length())):
-            self.positions.addPosition((topPositions.getPositionAt(i) + bottomPositions.getPositionAt(i)).multiply(0.5))
+    def __init__(self, num = None, timeInterval = None, topLeftPositions = None, bottomRightPositions = None, userType = userType2Num['unknown']):
+        super(BBAnnotation, self).__init__(num, timeInterval, userType = userType)
+        self.topLeftPositions = topLeftPositions.getPositions()
+        self.bottomRightPositions = bottomRightPositions.getPositions()
+
+    def computeCentroidTrajectory(self, homography = None):
+        self.positions = self.topLeftPositions.add(self.bottomRightPositions).multiply(0.5)
+        if homography != None:
+            self.positions = self.positions.project(homography)
+
+    def matches(self, obj, instant, matchingDistance):
+        '''Indicates if the annotation matches obj (MovingObject)
+        with threshold matchingDistance
+        Returns distance if below matchingDistance, matchingDistance+1 otherwise
+        (returns an actual value, otherwise munkres does not terminate)'''
+        d = Point.distanceNorm2(self.getPositionAtInstant(instant), obj.getPositionAtInstant(instant))
+        if d < matchingDistance:
+            return d
+        else:
+            return matchingDistance + 1
+
+def computeClearMOT(annotations, objects, matchingDistance, firstInstant, lastInstant, debug = False):
+    '''Computes the CLEAR MOT metrics 
+
+    Reference:
+    Keni, Bernardin, and Stiefelhagen Rainer. "Evaluating multiple object tracking performance: the CLEAR MOT metrics." EURASIP Journal on Image and Video Processing 2008 (2008)
+
+    objects and annotations are supposed to in the same space
+    current implementation is BBAnnotations (bounding boxes)
+    mathingDistance is threshold on matching between annotation and object
+
+    TO: tracker output (objects)
+    GT: ground truth (annotations)
+
+    Should we use the distance as weights or just 1/0 if distance below matchingDistance?
+    (add argument useDistanceForWeights = False)'''
+    from munkres import Munkres
+    
+    munk = Munkres()
+    dist = 0. # total distance between GT and TO
+    ct = 0 # number of associations between GT and tracker output in each frame
+    gt = 0 # number of GT.frames
+    mt = 0 # number of missed GT.frames (sum of the number of GT not detected in each frame)
+    fpt = 0 # number of false alarm.frames (tracker objects without match in each frame)
+    mme = 0 # number of mismatches
+    matches = {} # match[i] is the tracker track associated with GT i (using object references)
+    for t in xrange(firstInstant, lastInstant+1):
+        previousMatches = matches.copy()
+        # go through currently matched GT-TO and check if they are still matched withing matchingDistance
+        toDelete = []
+        for a in matches:
+            if a.existsAtInstant(t) and matches[a].existsAtInstant(t):
+                d = a.matches(matches[a], t, matchingDistance)
+                if d < matchingDistance:
+                    dist += d
+                else:
+                    toDelete.append(a)
+            else:
+                toDelete.append(a)
+        for a in toDelete:
+            del matches[a]
+
+        # match all unmatched GT-TO
+        matchedGTs = matches.keys()
+        matchedTOs = matches.values()
+        costs = []
+        unmatchedGTs = [a for a in annotations if a.existsAtInstant(t) and a not in matchedGTs]
+        unmatchedTOs = [o for o in objects if o.existsAtInstant(t) and o not in matchedTOs]
+        nGTs = len(matchedGTs)+len(unmatchedGTs)
+        nTOs = len(matchedTOs)+len(unmatchedTOs)
+        if len(unmatchedTOs) > 0:
+            for a in unmatchedGTs:
+                costs.append([a.matches(o, t, matchingDistance) for o in unmatchedTOs])
+        if len(costs) > 0:
+            newMatches = munk.compute(costs)
+            for k,v in newMatches:
+                if costs[k][v] < matchingDistance:
+                    matches[unmatchedGTs[k]]=unmatchedTOs[v]
+                    dist += costs[k][v]
+        if debug:
+            print('{} '.format(t)+', '.join(['{} {}'.format(k.getNum(), v.getNum()) for k,v in matches.iteritems()]))
         
+        # compute metrics elements
+        ct += len(matches)
+        mt += nGTs-len(matches)
+        fpt += nTOs-len(matches)
+        gt += nGTs
+        # compute mismatches
+        # for gt that do not appear in both frames, check if the corresponding to was matched to another gt in previous/next frame
+        mismatches = []
+        for a in matches:
+            if a in previousMatches:
+                if matches[a] != previousMatches[a]:
+                    mismatches.append(a)
+            elif matches[a] in previousMatches.values():
+                mismatches.append(matches[a])
+        for a in previousMatches:
+            if a not in matches and previousMatches[a] in matches.values():
+                mismatches.append(previousMatches[a])
+        if debug: 
+            for mm in set(mismatches):
+                print type(mm), mm.getNum()
+        # some object mismatches may appear twice
+        mme += len(set(mismatches))
+        
+    if ct > 0:
+        motp = dist/ct
+    else:
+        motp = None
+    if gt > 0:
+        mota = 1.-float(mt+fpt+mme)/gt
+    else:
+        mota = None
+    return motp, mota, mt, mme, fpt, gt
+
 
 def plotRoadUsers(objects, colors):
     '''Colors is a PlottingPropertyValues instance'''
--- a/python/objectsmoothing.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/objectsmoothing.py	Wed Dec 10 15:27:08 2014 -0500
@@ -1,234 +1,234 @@
-import storage, moving, utils
-from math import * #atan2,asin,degrees,sin,cos,pi
-import numpy as np
-
-import matplotlib.pyplot as plt
-
-def findNearest(feat, featureSet,t,reverse=True):
-	dist={}
-	for f in featureSet:
-		if reverse:
-			dist[f]= moving.Point.distanceNorm2(feat.getPositionAtInstant(t+1),f.getPositionAtInstant(t))
-		else:
-			dist[f]= moving.Point.distanceNorm2(feat.getPositionAtInstant(t-1),f.getPositionAtInstant(t))
-	return min(dist, key=dist.get) # = utils.argmaxDict(dist)
-	
-def getFeatures(obj,features,featureID):
-	#longestFeature = utils.argmaxDict({f:f.length() for i,f in enumerate(obj.features)})
-	t1,t3 = features[featureID].getFirstInstant(), features[featureID].getLastInstant()
-	listFeatures=[[features[featureID],t1,t3,moving.Point(0,0)]]
-	# find the features to fill in the beginning of the object existence
-	currentFeature = features[featureID]
-	while t1!=obj.getFirstInstant():
-		delta=listFeatures[-1][3]
-		featureSet = [f for f in obj.features if f.existsAtInstant(t1-1)]
-		feat = findNearest(currentFeature,featureSet,t1-1,reverse=True)
-		if feat.existsAtInstant(t1):
-			listFeatures.append([feat,feat.getFirstInstant(),t1-1,(currentFeature.getPositionAtInstant(t1)-feat.getPositionAtInstant(t1))+delta])
-		else:
-			listFeatures.append([feat,feat.getFirstInstant(),t1-1,(currentFeature.getPositionAtInstant(t1)-feat.getPositionAtInstant(t1-1))+delta])
-		currentFeature = feat
-		t1= feat.getFirstInstant()
-	# find the features to fill in the end of the object existence
-	delta=moving.Point(0,0)
-	currentFeature = features[featureID]
-	while t3!= obj.getLastInstant():
-		featureSet = [f for f in obj.features if f.existsAtInstant(t3+1)]
-		feat = findNearest(currentFeature,featureSet,t3+1,reverse=False)
-		if feat.existsAtInstant(t3):
-			listFeatures.append([feat,t3+1,feat.getLastInstant(),(currentFeature.getPositionAtInstant(t3)-feat.getPositionAtInstant(t3))+delta])
-		else:
-			listFeatures.append([feat,t3+1,feat.getLastInstant(),(currentFeature.getPositionAtInstant(t3)-feat.getPositionAtInstant(t3+1))+delta])
-		currentFeature = feat
-		t3= feat.getLastInstant()
-		delta=listFeatures[-1][3]
-	return listFeatures
-	
-def buildFeature(obj,features,featureID,num=1):
-	listFeatures= getFeatures(obj,features,featureID)
-	tmp={}
-	delta={}
-	for i in listFeatures:
-		for t in xrange(i[1],i[2]+1):
-			tmp[t]=[i[0],i[3]]
-	newTraj = moving.Trajectory()
-	
-	for instant in obj.getTimeInterval():
-		newTraj.addPosition(tmp[instant][0].getPositionAtInstant(instant)+tmp[instant][1])
-	newFeature= moving.MovingObject(num,timeInterval=obj.getTimeInterval(),positions=newTraj)
-	return newFeature
-
-def getBearing(p1,p2,p3):
-	angle = degrees(atan2(p3.y -p1.y, p3.x -p1.x))
-	bearing1 = (90 - angle) % 360
-	angle2 = degrees(atan2(p2.y -p1.y, p2.x -p1.x))
-	bearing2 = (90 - angle2) % 360	
-	dist= moving.Point.distanceNorm2(p1, p2)
-	return [dist,bearing1,bearing2,bearing2-bearing1]	
-
-#Quantitative analysis "CSJ" functions	
-def computeVelocities (object,smoothing=True,halfWidth=3):  #compute velocities from positions
-	velocities={}
-	for i in list(object.timeInterval)[:-1]:
-		p1= object.getPositionAtInstant(i)
-		p2= object.getPositionAtInstant(i+1)
-		velocities[i]=p2-p1		
-	velocities[object.getLastInstant()]= velocities[object.getLastInstant()-1]  # duplicate last point
-	if smoothing:
-		velX= [velocities[y].aslist()[0] for y in sorted(velocities.keys())]
-		velY= [velocities[y].aslist()[1] for y in sorted(velocities.keys())]
-		v1= list(utils.filterMovingWindow(velX, halfWidth))
-		v2= list(utils.filterMovingWindow(velY, halfWidth))
-		smoothedVelocity={}
-		for t,i in enumerate(sorted(velocities.keys())):
-			smoothedVelocity[i]=moving.Point(v1[t], v2[t])
-		velocities=smoothedVelocity
-	return velocities
-	
-def computeAcceleration (object,fromPosition=True):
-	acceleration={}
-	if fromPosition:
-		velocities=computeVelocities(object,False,1)
-		for i in sorted (velocities.keys()):
-			if i != sorted (velocities.keys())[-1]:
-				acceleration[i]= velocities[i+1]-velocities[i]
-	else:
-		for i in list(object.timeInterval)[:-1]:
-			v1= object.getVelocityAtInstant(i)
-			v2= object.getVelocityAtInstant(i+1)
-			acceleration[i]= v2-v1
-	return acceleration
-	
-def computeJerk (object,fromPosition=True):
-	jerk={}
-	acceleration=computeAcceleration (object,fromPosition=fromPosition)
-	for i in sorted (acceleration.keys()):
-		if i != sorted (acceleration.keys())[-1]:
-			jerk[i]= (acceleration[i+1]-acceleration[i]).norm2()
-	return jerk
-	
-def sumSquaredJerk (object,fromPosition=True):
-	jerk= computeJerk (object,fromPosition=fromPosition)
-	t=0
-	for i in sorted(jerk.keys()):
-		t+= jerk[i]* jerk[i]
-	return t
-	
-def smoothObjectTrajectory(obj,features,featureID,newNum,smoothing=False,halfWidth=3,create=False):
-	results=[]	
-	bearing={}
-	if create:
-		feature= buildFeature(obj,features,featureID,num=1)
-	else:
-		feature=features[featureID]
-	for t in feature.getTimeInterval():
-		p1= feature.getPositionAtInstant(t)
-		p2= obj.getPositionAtInstant(t)
-		if t!=feature.getLastInstant():
-			p3= feature.getPositionAtInstant(t+1)
-		else:
-			p1= feature.getPositionAtInstant(t-1)
-			p3= feature.getPositionAtInstant(t)
-		bearing[t]= getBearing(p1,p2,p3)[1]		
-		results.append(getBearing(p1,p2,p3))
-			
-
-	medianResults=np.median(results,0)
-	dist= medianResults[0]
-	angle= medianResults[3]
-	
-	for i in sorted(bearing.keys()):
-		bearing[i]= bearing[i]+angle
-
-	if smoothing:
-		bearingInput=[]
-		for i in sorted(bearing.keys()):
-			bearingInput.append(bearing[i])
-		import utils
-		bearingOut=utils.filterMovingWindow(bearingInput, halfWidth)
-		for t,i in enumerate(sorted(bearing.keys())):
-			bearing[i]=bearingOut[t]
-		
-		#solve a smoothing problem in case of big drop in computing bearing (0,360)	
-		for t,i in enumerate(sorted(bearing.keys())):
-			if i!= max(bearing.keys()) and abs(bearingInput[t] - bearingInput[t+1])>=340:
-					for x in xrange(max(i-halfWidth,min(bearing.keys())),min(i+halfWidth,max(bearing.keys()))+1):
-						bearing[x]=bearingInput[t-i+x]
-
-	translated = moving.Trajectory()
-	for t in feature.getTimeInterval():
-		p1= feature.getPositionAtInstant(t)
-		p1.x = p1.x + dist*sin(bearing[t]*pi/180)
-		p1.y = p1.y + dist*cos(bearing[t]*pi/180)
-		translated.addPosition(p1)
-		
-	#modify first and last un-smoothed positions (half width)
-	if smoothing:
-		d1= translated[halfWidth]- feature.positions[halfWidth]
-		d2= translated[-halfWidth-1]- feature.positions[-halfWidth-1]
-		for i in xrange(halfWidth):
-			p1= feature.getPositionAt(i)+d1
-			p2= feature.getPositionAt(-i-1)+d2
-			translated.setPosition(i,p1)
-			translated.setPosition(-i-1,p2)
-		
-	newObj= moving.MovingObject(newNum,timeInterval=feature.getTimeInterval(),positions=translated)
-	return newObj
-	
-def smoothObjectTrajectory(obj,features,newNum,minLengthParam=0.7,smoothing=False,plotResults=True,halfWidth=3,computeVelocities=True,optimize=True,create=False):
-	featureList=[i.num for i in obj.features if i.length() >= minLengthParam*obj.length()]
-	if featureList==[]:
-		featureList.append(longestFeature(obj))
-		create=True
-	objs=[]
-	for featureID in featureList:
-		objTMP=smoothObjectTrajectory(obj,features,featureID,newNum,smoothing=smoothing,halfWidth=halfWidth,create=create)
-		objs.append(objTMP)
-	newTranslated = moving.Trajectory()
-	newInterval=[]
-	for t in obj.timeInterval:
-		xCoord=[]
-		yCoord=[]
-		for i in objs:
-			if i.existsAtInstant(t):
-				p1= i.getPositionAtInstant(t)
-				xCoord.append(p1.x)
-				yCoord.append(p1.y)
-		if xCoord!=[]:
-			tmp= moving.Point(np.median(xCoord),np.median(yCoord))
-			newInterval.append(t)
-			newTranslated.addPosition(tmp)
-	
-	newObj= moving.MovingObject(newNum,timeInterval=moving.TimeInterval(min(newInterval),max(newInterval)),positions=newTranslated)
-		
-	if computeVelocities:
-		tmpTraj = moving.Trajectory()
-		velocities= computeVelocities(newObj,True,5)
-		for i in sorted(velocities.keys()):
-			tmpTraj.addPosition(velocities[i])
-		newObj.velocities=tmpTraj
-	else:
-		newObj.velocities=obj.velocities
-	
-	if optimize:
-		csj1= sumSquaredJerk (obj,fromPosition=True)
-		csj2= sumSquaredJerk (newObj,fromPosition=True)
-		if csj1<csj2:
-			newObj=obj
-			newObj.velocities=obj.velocities
-		if computeVelocities and csj1>=csj2:
-			csj3= sumSquaredJerk (obj,fromPosition=False)
-			csj4= sumSquaredJerk (newObj,fromPosition=False)
-			if csj4<=csj3:
-				newObj.velocities= obj.velocities
-	newObj.featureNumbers=obj.featureNumbers
-	newObj.features=obj.features
-	newObj.userType=obj.userType
-	if plotResults:
-		plt.figure()
-		plt.title('objects_id = {}'.format(obj.num))
-		for i in featureList:
-			features[i].plot('cx-')
-		obj.plot('rx-')
-		newObj.plot('gx-')		
-	return newObj
\ No newline at end of file
+import storage, moving, utils
+from math import * #atan2,asin,degrees,sin,cos,pi
+import numpy as np
+
+import matplotlib.pyplot as plt
+
+def findNearest(feat, featureSet,t,reverse=True):
+    dist={}
+    for f in featureSet:
+        if reverse:
+            dist[f]= moving.Point.distanceNorm2(feat.getPositionAtInstant(t+1),f.getPositionAtInstant(t))
+        else:
+            dist[f]= moving.Point.distanceNorm2(feat.getPositionAtInstant(t-1),f.getPositionAtInstant(t))
+    return min(dist, key=dist.get) # = utils.argmaxDict(dist)
+    
+def getFeatures(obj,features,featureID):
+    #longestFeature = utils.argmaxDict({f:f.length() for i,f in enumerate(obj.features)})
+    t1,t3 = features[featureID].getFirstInstant(), features[featureID].getLastInstant()
+    listFeatures=[[features[featureID],t1,t3,moving.Point(0,0)]]
+    # find the features to fill in the beginning of the object existence
+    currentFeature = features[featureID]
+    while t1!=obj.getFirstInstant():
+        delta=listFeatures[-1][3]
+        featureSet = [f for f in obj.features if f.existsAtInstant(t1-1)]
+        feat = findNearest(currentFeature,featureSet,t1-1,reverse=True)
+        if feat.existsAtInstant(t1):
+            listFeatures.append([feat,feat.getFirstInstant(),t1-1,(currentFeature.getPositionAtInstant(t1)-feat.getPositionAtInstant(t1))+delta])
+        else:
+            listFeatures.append([feat,feat.getFirstInstant(),t1-1,(currentFeature.getPositionAtInstant(t1)-feat.getPositionAtInstant(t1-1))+delta])
+        currentFeature = feat
+        t1= feat.getFirstInstant()
+    # find the features to fill in the end of the object existence
+    delta=moving.Point(0,0)
+    currentFeature = features[featureID]
+    while t3!= obj.getLastInstant():
+        featureSet = [f for f in obj.features if f.existsAtInstant(t3+1)]
+        feat = findNearest(currentFeature,featureSet,t3+1,reverse=False)
+        if feat.existsAtInstant(t3):
+            listFeatures.append([feat,t3+1,feat.getLastInstant(),(currentFeature.getPositionAtInstant(t3)-feat.getPositionAtInstant(t3))+delta])
+        else:
+            listFeatures.append([feat,t3+1,feat.getLastInstant(),(currentFeature.getPositionAtInstant(t3)-feat.getPositionAtInstant(t3+1))+delta])
+        currentFeature = feat
+        t3= feat.getLastInstant()
+        delta=listFeatures[-1][3]
+    return listFeatures
+    
+def buildFeature(obj,features,featureID,num=1):
+    listFeatures= getFeatures(obj,features,featureID)
+    tmp={}
+    delta={}
+    for i in listFeatures:
+        for t in xrange(i[1],i[2]+1):
+            tmp[t]=[i[0],i[3]]
+    newTraj = moving.Trajectory()
+    
+    for instant in obj.getTimeInterval():
+        newTraj.addPosition(tmp[instant][0].getPositionAtInstant(instant)+tmp[instant][1])
+    newFeature= moving.MovingObject(num,timeInterval=obj.getTimeInterval(),positions=newTraj)
+    return newFeature
+
+def getBearing(p1,p2,p3):
+    angle = degrees(atan2(p3.y -p1.y, p3.x -p1.x))
+    bearing1 = (90 - angle) % 360
+    angle2 = degrees(atan2(p2.y -p1.y, p2.x -p1.x))
+    bearing2 = (90 - angle2) % 360    
+    dist= moving.Point.distanceNorm2(p1, p2)
+    return [dist,bearing1,bearing2,bearing2-bearing1]    
+
+#Quantitative analysis "CSJ" functions    
+def computeVelocities (object,smoothing=True,halfWidth=3):  #compute velocities from positions
+    velocities={}
+    for i in list(object.timeInterval)[:-1]:
+        p1= object.getPositionAtInstant(i)
+        p2= object.getPositionAtInstant(i+1)
+        velocities[i]=p2-p1        
+    velocities[object.getLastInstant()]= velocities[object.getLastInstant()-1]  # duplicate last point
+    if smoothing:
+        velX= [velocities[y].aslist()[0] for y in sorted(velocities.keys())]
+        velY= [velocities[y].aslist()[1] for y in sorted(velocities.keys())]
+        v1= list(utils.filterMovingWindow(velX, halfWidth))
+        v2= list(utils.filterMovingWindow(velY, halfWidth))
+        smoothedVelocity={}
+        for t,i in enumerate(sorted(velocities.keys())):
+            smoothedVelocity[i]=moving.Point(v1[t], v2[t])
+        velocities=smoothedVelocity
+    return velocities
+    
+def computeAcceleration (object,fromPosition=True):
+    acceleration={}
+    if fromPosition:
+        velocities=computeVelocities(object,False,1)
+        for i in sorted (velocities.keys()):
+            if i != sorted (velocities.keys())[-1]:
+                acceleration[i]= velocities[i+1]-velocities[i]
+    else:
+        for i in list(object.timeInterval)[:-1]:
+            v1= object.getVelocityAtInstant(i)
+            v2= object.getVelocityAtInstant(i+1)
+            acceleration[i]= v2-v1
+    return acceleration
+    
+def computeJerk (object,fromPosition=True):
+    jerk={}
+    acceleration=computeAcceleration (object,fromPosition=fromPosition)
+    for i in sorted (acceleration.keys()):
+        if i != sorted (acceleration.keys())[-1]:
+            jerk[i]= (acceleration[i+1]-acceleration[i]).norm2()
+    return jerk
+    
+def sumSquaredJerk (object,fromPosition=True):
+    jerk= computeJerk (object,fromPosition=fromPosition)
+    t=0
+    for i in sorted(jerk.keys()):
+        t+= jerk[i]* jerk[i]
+    return t
+    
+def smoothObjectTrajectory(obj,features,featureID,newNum,smoothing=False,halfWidth=3,create=False):
+    results=[]    
+    bearing={}
+    if create:
+        feature= buildFeature(obj,features,featureID,num=1)
+    else:
+        feature=features[featureID]
+    for t in feature.getTimeInterval():
+        p1= feature.getPositionAtInstant(t)
+        p2= obj.getPositionAtInstant(t)
+        if t!=feature.getLastInstant():
+            p3= feature.getPositionAtInstant(t+1)
+        else:
+            p1= feature.getPositionAtInstant(t-1)
+            p3= feature.getPositionAtInstant(t)
+        bearing[t]= getBearing(p1,p2,p3)[1]        
+        results.append(getBearing(p1,p2,p3))
+            
+
+    medianResults=np.median(results,0)
+    dist= medianResults[0]
+    angle= medianResults[3]
+    
+    for i in sorted(bearing.keys()):
+        bearing[i]= bearing[i]+angle
+
+    if smoothing:
+        bearingInput=[]
+        for i in sorted(bearing.keys()):
+            bearingInput.append(bearing[i])
+        import utils
+        bearingOut=utils.filterMovingWindow(bearingInput, halfWidth)
+        for t,i in enumerate(sorted(bearing.keys())):
+            bearing[i]=bearingOut[t]
+        
+        #solve a smoothing problem in case of big drop in computing bearing (0,360)    
+        for t,i in enumerate(sorted(bearing.keys())):
+            if i!= max(bearing.keys()) and abs(bearingInput[t] - bearingInput[t+1])>=340:
+                for x in xrange(max(i-halfWidth,min(bearing.keys())),min(i+halfWidth,max(bearing.keys()))+1):
+                    bearing[x]=bearingInput[t-i+x]
+
+    translated = moving.Trajectory()
+    for t in feature.getTimeInterval():
+        p1= feature.getPositionAtInstant(t)
+        p1.x = p1.x + dist*sin(bearing[t]*pi/180)
+        p1.y = p1.y + dist*cos(bearing[t]*pi/180)
+        translated.addPosition(p1)
+        
+    #modify first and last un-smoothed positions (half width)
+    if smoothing:
+        d1= translated[halfWidth]- feature.positions[halfWidth]
+        d2= translated[-halfWidth-1]- feature.positions[-halfWidth-1]
+        for i in xrange(halfWidth):
+            p1= feature.getPositionAt(i)+d1
+            p2= feature.getPositionAt(-i-1)+d2
+            translated.setPosition(i,p1)
+            translated.setPosition(-i-1,p2)
+        
+    newObj= moving.MovingObject(newNum,timeInterval=feature.getTimeInterval(),positions=translated)
+    return newObj
+    
+def smoothObjectTrajectory(obj,features,newNum,minLengthParam=0.7,smoothing=False,plotResults=True,halfWidth=3,computeVelocities=True,optimize=True,create=False):
+    featureList=[i.num for i in obj.features if i.length() >= minLengthParam*obj.length()]
+    if featureList==[]:
+        featureList.append(longestFeature(obj))
+        create=True
+    objs=[]
+    for featureID in featureList:
+        objTMP=smoothObjectTrajectory(obj,features,featureID,newNum,smoothing=smoothing,halfWidth=halfWidth,create=create)
+        objs.append(objTMP)
+    newTranslated = moving.Trajectory()
+    newInterval=[]
+    for t in obj.timeInterval:
+        xCoord=[]
+        yCoord=[]
+        for i in objs:
+            if i.existsAtInstant(t):
+                p1= i.getPositionAtInstant(t)
+                xCoord.append(p1.x)
+                yCoord.append(p1.y)
+        if xCoord!=[]:
+            tmp= moving.Point(np.median(xCoord),np.median(yCoord))
+            newInterval.append(t)
+            newTranslated.addPosition(tmp)
+    
+    newObj= moving.MovingObject(newNum,timeInterval=moving.TimeInterval(min(newInterval),max(newInterval)),positions=newTranslated)
+        
+    if computeVelocities:
+        tmpTraj = moving.Trajectory()
+        velocities= computeVelocities(newObj,True,5)
+        for i in sorted(velocities.keys()):
+            tmpTraj.addPosition(velocities[i])
+        newObj.velocities=tmpTraj
+    else:
+        newObj.velocities=obj.velocities
+    
+    if optimize:
+        csj1= sumSquaredJerk (obj,fromPosition=True)
+        csj2= sumSquaredJerk (newObj,fromPosition=True)
+        if csj1<csj2:
+            newObj=obj
+            newObj.velocities=obj.velocities
+        if computeVelocities and csj1>=csj2:
+            csj3= sumSquaredJerk (obj,fromPosition=False)
+            csj4= sumSquaredJerk (newObj,fromPosition=False)
+            if csj4<=csj3:
+                newObj.velocities= obj.velocities
+    newObj.featureNumbers=obj.featureNumbers
+    newObj.features=obj.features
+    newObj.userType=obj.userType
+    if plotResults:
+        plt.figure()
+        plt.title('objects_id = {}'.format(obj.num))
+        for i in featureList:
+            features[i].plot('cx-')
+        obj.plot('rx-')
+        newObj.plot('gx-')        
+    return newObj
--- a/python/poly-utils.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/poly-utils.py	Wed Dec 10 15:27:08 2014 -0500
@@ -33,8 +33,8 @@
         values= {}
         for i,t in enumerate(indicatorFrameNums):
             values[t] = [data[i,index] for index in selectedIndicators]
-        inter.addIndicator(SeverityIndicator('selectedIndicators', values))	
-		
+        inter.addIndicator(SeverityIndicator('selectedIndicators', values))    
+        
     #interactions.append(inter)
     file.close()
     #return interactions
@@ -118,9 +118,9 @@
             else:
                 for i in xrange(time[0],time[-1]+1):
                     try:
-						tmp = getDataAtInstant(roadUserData, i)
-						values[i] = np.sum(tmp[:,5]*tmp[:,6])/np.sum(tmp[:,5])/frameRate
+                        tmp = getDataAtInstant(roadUserData, i)
+                        values[i] = np.sum(tmp[:,5]*tmp[:,6])/np.sum(tmp[:,5])/frameRate
                     except IOError:
-                    	values[i] = np.inf
+                        values[i] = np.inf
                 return SeverityIndicator(indicatorName, values, mostSevereIsMax = False), roadUserData
-    return None, None
\ No newline at end of file
+    return None, None
--- a/python/prediction.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/prediction.py	Wed Dec 10 15:27:08 2014 -0500
@@ -48,14 +48,14 @@
 
     def getControl(self):
         return self.control
-		
+        
 def findNearestParams(initialPosition,prototypeTrajectory):
-	''' nearest parameters are the index of minDistance and the orientation  '''
-	distances=[]
-	for position in prototypeTrajectory.positions:
-		distances.append(moving.Point.distanceNorm2(initialPosition, position))
-	minDistanceIndex= np.argmin(distances)
-	return minDistanceIndex, moving.NormAngle.fromPoint(prototypeTrajectory.velocities[minDistanceIndex]).angle
+    ''' nearest parameters are the index of minDistance and the orientation  '''
+    distances=[]
+    for position in prototypeTrajectory.positions:
+        distances.append(moving.Point.distanceNorm2(initialPosition, position))
+    minDistanceIndex= np.argmin(distances)
+    return minDistanceIndex, moving.NormAngle.fromPoint(prototypeTrajectory.velocities[minDistanceIndex]).angle
 
 class PredictedTrajectoryPrototype(PredictedTrajectory):
     '''Predicted trajectory that follows a prototype trajectory
@@ -84,7 +84,7 @@
                 anglePrototype = findNearestParams(self.predictedPositions[nTimeSteps-1],self.prototypeTrajectory)[1]
                 self.predictedSpeedOrientations[nTimeSteps]= moving.NormAngle(speedNorm, anglePrototype)
                 self.predictedPositions[nTimeSteps],tmp= moving.predictPosition(self.predictedPositions[nTimeSteps-1], self.predictedSpeedOrientations[nTimeSteps-1], moving.NormAngle(0,0), None)
-			
+            
             else: # see c++ code, calculate ratio
                 speedNorm= self.predictedSpeedOrientations[0].norm
                 instant=findNearestParams(self.predictedPositions[0],self.prototypeTrajectory)[0]
@@ -94,7 +94,7 @@
                 anglePrototype = findNearestParams(self.predictedPositions[nTimeSteps-1],self.prototypeTrajectory)[1]
                 if nTimeSteps<len(resampledSpeeds):
                     self.predictedSpeedOrientations[nTimeSteps]= moving.NormAngle(resampledSpeeds[nTimeSteps], anglePrototype)
-                    self.predictedPositions[nTimeSteps],tmp= moving.predictPosition(self.predictedPositions[nTimeSteps-1], self.predictedSpeedOrientations[nTimeSteps-1], moving.NormAngle(0,0), None)				
+                    self.predictedPositions[nTimeSteps],tmp= moving.predictPosition(self.predictedPositions[nTimeSteps-1], self.predictedSpeedOrientations[nTimeSteps-1], moving.NormAngle(0,0), None)                
                 else:
                     self.predictedSpeedOrientations[nTimeSteps]= moving.NormAngle(resampledSpeeds[-1], anglePrototype)
                     self.predictedPositions[nTimeSteps],tmp= moving.predictPosition(self.predictedPositions[nTimeSteps-1], self.predictedSpeedOrientations[nTimeSteps-1], moving.NormAngle(0,0), None)
@@ -168,93 +168,93 @@
     close()
 
 def calculateProbability(nMatching,similarity,objects):
-	sumFrequencies=sum([nMatching[p] for p in similarity.keys()])
-	prototypeProbability={}
-	for i in similarity.keys():
-		prototypeProbability[i]= similarity[i] * float(nMatching[i])/sumFrequencies
-	sumProbabilities= sum([prototypeProbability[p] for p in prototypeProbability.keys()])
-	probabilities={}
-	for i in prototypeProbability.keys():
-		probabilities[objects[i]]= float(prototypeProbability[i])/sumProbabilities
-	return probabilities
+    sumFrequencies=sum([nMatching[p] for p in similarity.keys()])
+    prototypeProbability={}
+    for i in similarity.keys():
+        prototypeProbability[i]= similarity[i] * float(nMatching[i])/sumFrequencies
+    sumProbabilities= sum([prototypeProbability[p] for p in prototypeProbability.keys()])
+    probabilities={}
+    for i in prototypeProbability.keys():
+        probabilities[objects[i]]= float(prototypeProbability[i])/sumProbabilities
+    return probabilities
 
 def findPrototypes(prototypes,nMatching,objects,route,partialObjPositions,noiseEntryNums,noiseExitNums,minSimilarity=0.1,mostMatched=None,spatialThreshold=1.0, delta=180):
-	''' behaviour prediction first step'''
-	if route[0] not in noiseEntryNums: 
-		prototypesRoutes= [ x for x in sorted(prototypes.keys()) if route[0]==x[0]]
-	elif route[1] not in noiseExitNums:
-		prototypesRoutes=[ x for x in sorted(prototypes.keys()) if route[1]==x[1]]
-	else:
-		prototypesRoutes=[x for x in sorted(prototypes.keys())]
-	lcss = LCSS(similarityFunc=lambda x,y: (distanceForLCSS(x,y) <= spatialThreshold),delta=delta)
-	similarity={}
-	for y in prototypesRoutes: 
-		if y in prototypes.keys():
-			prototypesIDs=prototypes[y]			
-			for x in prototypesIDs:
-				s=lcss.computeNormalized(partialObjPositions, objects[x].positions)
-				if s >= minSimilarity:
-					similarity[x]=s
-	
-	if mostMatched==None:
-		probabilities= calculateProbability(nMatching,similarity,objects)		
-		return probabilities
-	else:
-		mostMatchedValues=sorted(similarity.values(),reverse=True)[:mostMatched]
-		keys=[k for k in similarity.keys() if similarity[k] in mostMatchedValues]
-		newSimilarity={}
-		for i in keys:
-			newSimilarity[i]=similarity[i]
-		probabilities= calculateProbability(nMatching,newSimilarity,objects)		
-		return probabilities		
-		
+    ''' behaviour prediction first step'''
+    if route[0] not in noiseEntryNums: 
+        prototypesRoutes= [ x for x in sorted(prototypes.keys()) if route[0]==x[0]]
+    elif route[1] not in noiseExitNums:
+        prototypesRoutes=[ x for x in sorted(prototypes.keys()) if route[1]==x[1]]
+    else:
+        prototypesRoutes=[x for x in sorted(prototypes.keys())]
+    lcss = LCSS(similarityFunc=lambda x,y: (distanceForLCSS(x,y) <= spatialThreshold),delta=delta)
+    similarity={}
+    for y in prototypesRoutes: 
+        if y in prototypes.keys():
+            prototypesIDs=prototypes[y]            
+            for x in prototypesIDs:
+                s=lcss.computeNormalized(partialObjPositions, objects[x].positions)
+                if s >= minSimilarity:
+                    similarity[x]=s
+    
+    if mostMatched==None:
+        probabilities= calculateProbability(nMatching,similarity,objects)        
+        return probabilities
+    else:
+        mostMatchedValues=sorted(similarity.values(),reverse=True)[:mostMatched]
+        keys=[k for k in similarity.keys() if similarity[k] in mostMatchedValues]
+        newSimilarity={}
+        for i in keys:
+            newSimilarity[i]=similarity[i]
+        probabilities= calculateProbability(nMatching,newSimilarity,objects)        
+        return probabilities        
+        
 def findPrototypesSpeed(prototypes,secondStepPrototypes,nMatching,objects,route,partialObjPositions,noiseEntryNums,noiseExitNums,minSimilarity=0.1,mostMatched=None,useDestination=True,spatialThreshold=1.0, delta=180):
-	if useDestination:
-		prototypesRoutes=[route]
-	else:
-		if route[0] not in noiseEntryNums: 
-			prototypesRoutes= [ x for x in sorted(prototypes.keys()) if route[0]==x[0]]
-		elif route[1] not in noiseExitNums:
-			prototypesRoutes=[ x for x in sorted(prototypes.keys()) if route[1]==x[1]]
-		else:
-			prototypesRoutes=[x for x in sorted(prototypes.keys())]
-	lcss = LCSS(similarityFunc=lambda x,y: (distanceForLCSS(x,y) <= spatialThreshold),delta=delta)
-	similarity={}
-	for y in prototypesRoutes: 
-		if y in prototypes.keys():
-			prototypesIDs=prototypes[y]	
-			for x in prototypesIDs:
-				s=lcss.computeNormalized(partialObjPositions, objects[x].positions)
-				if s >= minSimilarity:
-					similarity[x]=s
-	
-	newSimilarity={}
-	for i in similarity.keys():
-		if i in secondStepPrototypes.keys():
-			for j in secondStepPrototypes[i]:
-				newSimilarity[j]=similarity[i]
-	probabilities= calculateProbability(nMatching,newSimilarity,objects)		
-	return probabilities
-	
+    if useDestination:
+        prototypesRoutes=[route]
+    else:
+        if route[0] not in noiseEntryNums: 
+            prototypesRoutes= [ x for x in sorted(prototypes.keys()) if route[0]==x[0]]
+        elif route[1] not in noiseExitNums:
+            prototypesRoutes=[ x for x in sorted(prototypes.keys()) if route[1]==x[1]]
+        else:
+            prototypesRoutes=[x for x in sorted(prototypes.keys())]
+    lcss = LCSS(similarityFunc=lambda x,y: (distanceForLCSS(x,y) <= spatialThreshold),delta=delta)
+    similarity={}
+    for y in prototypesRoutes: 
+        if y in prototypes.keys():
+            prototypesIDs=prototypes[y]    
+            for x in prototypesIDs:
+                s=lcss.computeNormalized(partialObjPositions, objects[x].positions)
+                if s >= minSimilarity:
+                    similarity[x]=s
+    
+    newSimilarity={}
+    for i in similarity.keys():
+        if i in secondStepPrototypes.keys():
+            for j in secondStepPrototypes[i]:
+                newSimilarity[j]=similarity[i]
+    probabilities= calculateProbability(nMatching,newSimilarity,objects)        
+    return probabilities
+    
 def getPrototypeTrajectory(obj,route,currentInstant,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity=0.1,mostMatched=None,useDestination=True,useSpeedPrototype=True):
     partialInterval=moving.Interval(obj.getFirstInstant(),currentInstant)
-    partialObjPositions= obj.getObjectInTimeInterval(partialInterval).positions	
+    partialObjPositions= obj.getObjectInTimeInterval(partialInterval).positions    
     if useSpeedPrototype:
         prototypeTrajectories=findPrototypesSpeed(prototypes,secondStepPrototypes,nMatching,objects,route,partialObjPositions,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination)
     else:
         prototypeTrajectories=findPrototypes(prototypes,nMatching,objects,route,partialObjPositions,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched)
-    return 	prototypeTrajectories
+    return prototypeTrajectories
 
 def computeCrossingsCollisionsAtInstant(predictionParams,currentInstant, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ = False, debug = False,usePrototypes=True,route1= (-1,-1),route2=(-1,-1),prototypes={},secondStepPrototypes={},nMatching={},objects=[],noiseEntryNums=[],noiseExitNums=[],minSimilarity=0.1,mostMatched=None,useDestination=True,useSpeedPrototype=True):  
     '''returns the lists of collision points and crossing zones'''
     if usePrototypes:
-		prototypeTrajectories1=getPrototypeTrajectory(obj1,route1,currentInstant,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)
-		prototypeTrajectories2= getPrototypeTrajectory(obj2,route2,currentInstant,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)
-		predictedTrajectories1 = predictionParams.generatePredictedTrajectories(obj1, currentInstant,prototypeTrajectories1)
-		predictedTrajectories2 = predictionParams.generatePredictedTrajectories(obj2, currentInstant,prototypeTrajectories2) 	
+        prototypeTrajectories1=getPrototypeTrajectory(obj1,route1,currentInstant,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)
+        prototypeTrajectories2= getPrototypeTrajectory(obj2,route2,currentInstant,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)
+        predictedTrajectories1 = predictionParams.generatePredictedTrajectories(obj1, currentInstant,prototypeTrajectories1)
+        predictedTrajectories2 = predictionParams.generatePredictedTrajectories(obj2, currentInstant,prototypeTrajectories2)     
     else:
         predictedTrajectories1 = predictionParams.generatePredictedTrajectories(obj1, currentInstant)
-        predictedTrajectories2 = predictionParams.generatePredictedTrajectories(obj2, currentInstant)		
+        predictedTrajectories2 = predictionParams.generatePredictedTrajectories(obj2, currentInstant)        
 
     collisionPoints = []
     crossingZones = []
@@ -276,7 +276,7 @@
                         #if (et1.predictPosition(t1)-et2.predictPosition(t2)).norm2() < collisionDistanceThreshold:
                         #    cz = (et1.predictPosition(t1)+et2.predictPosition(t2)).multiply(0.5)
                         cz = moving.segmentIntersection(et1.predictPosition(t1), et1.predictPosition(t1+1), et2.predictPosition(t2), et2.predictPosition(t2+1))
-                        if cz:
+                        if cz != None:
                             deltaV= (et1.predictPosition(t1)- et1.predictPosition(t1+1) - et2.predictPosition(t2)+ et2.predictPosition(t2+1)).norm2()
                             crossingZones.append(SafetyPoint(cz, et1.probability*et2.probability, abs(t1-t2)-(float(collisionDistanceThreshold)/deltaV)))
                         t2 += 1
@@ -284,7 +284,8 @@
 
     if debug:
         savePredictedTrajectoriesFigure(currentInstant, obj1, obj2, predictedTrajectories1, predictedTrajectories2, timeHorizon)
-    return currentInstant,collisionPoints, crossingZones
+
+    return currentInstant, collisionPoints, crossingZones
 
 def computeCrossingsCollisions(predictionParams, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ = False, debug = False, timeInterval = None,nProcesses = 1,usePrototypes=True,route1= (-1,-1),route2=(-1,-1),prototypes={},secondStepPrototypes={},nMatching={},objects=[],noiseEntryNums=[],noiseExitNums=[],minSimilarity=0.1,mostMatched=None,useDestination=True,useSpeedPrototype=True,acceptPartialLength=30, step=1):
     '''Computes all crossing and collision points at each common instant for two road users. '''
@@ -312,7 +313,7 @@
                         if len(cp) != 0:
                             collisionPoints[i] = cp
                         if len(cz) != 0:
-                            crossingZones[i] = cz						
+                            crossingZones[i] = cz                        
         else:
             for i in list(commonTimeInterval)[:-1]: # do not look at the 1 last position/velocities, often with errors
                 i, cp, cz = computeCrossingsCollisionsAtInstant(predictionParams, i, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ, debug,usePrototypes,route1,route2,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)
@@ -334,7 +335,7 @@
             if len(cz) != 0:
                 crossingZones[i] = cz
         pool.close()
-    return collisionPoints, crossingZones            
+    return collisionPoints, crossingZones
 
 class PredictionParameters:
     def __init__(self, name, maxSpeed):
@@ -557,19 +558,19 @@
 ####
 # Other Methods
 ####
-class prototypePredictionParameters(PredictionParameters):
-    def __init__(self, maxSpeed, nPredictedTrajectories,constantSpeed = True):
+class PrototypePredictionParameters(PredictionParameters):
+    def __init__(self, maxSpeed, nPredictedTrajectories, constantSpeed = True):
         name = 'prototype'
         PredictionParameters.__init__(self, name, maxSpeed)
         self.nPredictedTrajectories = nPredictedTrajectories
-        self.constantSpeed = constantSpeed		
+        self.constantSpeed = constantSpeed
         
     def generatePredictedTrajectories(self, obj, instant,prototypeTrajectories):
         predictedTrajectories = []
         initialPosition = obj.getPositionAtInstant(instant)
         initialVelocity = obj.getVelocityAtInstant(instant)
         for prototypeTraj in prototypeTrajectories.keys():
-			predictedTrajectories.append(PredictedTrajectoryPrototype(initialPosition, initialVelocity, prototypeTraj, constantSpeed = self.constantSpeed, probability = prototypeTrajectories[prototypeTraj])) 
+            predictedTrajectories.append(PredictedTrajectoryPrototype(initialPosition, initialVelocity, prototypeTraj, constantSpeed = self.constantSpeed, probability = prototypeTrajectories[prototypeTraj])) 
         return predictedTrajectories
 
 if __name__ == "__main__":
--- a/python/run-tests.sh	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/run-tests.sh	Wed Dec 10 15:27:08 2014 -0500
@@ -4,4 +4,3 @@
 do
     python $f
 done
-rm nonexistent
--- a/python/storage.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/storage.py	Wed Dec 10 15:27:08 2014 -0500
@@ -64,7 +64,7 @@
                     
     connection.commit()
     connection.close()
-	
+    
 def writeFeaturesToSqlite(objects, outputFilename, trajectoryType, objectNumbers = -1):
     '''write features trajectories maintain trajectory ID,velocities dataset  '''
     connection = sqlite3.connect(outputFilename)
@@ -72,7 +72,7 @@
 
     cursor.execute("CREATE TABLE IF NOT EXISTS \"positions\"(trajectory_id INTEGER,frame_number INTEGER, x_coordinate REAL, y_coordinate REAL, PRIMARY KEY(trajectory_id, frame_number))")
     cursor.execute("CREATE TABLE IF NOT EXISTS \"velocities\"(trajectory_id INTEGER,frame_number INTEGER, x_coordinate REAL, y_coordinate REAL, PRIMARY KEY(trajectory_id, frame_number))")
-	
+    
     if trajectoryType == 'feature':
         if type(objectNumbers) == int and objectNumbers == -1:
             for trajectory in objects:
@@ -85,14 +85,14 @@
                     
     connection.commit()
     connection.close()
-	
+    
 def writePrototypesToSqlite(prototypes,nMatching, outputFilename):
     """ prototype dataset is a dictionary with  keys== routes, values== prototypes Ids """
     connection = sqlite3.connect(outputFilename)
     cursor = connection.cursor()
 
     cursor.execute("CREATE TABLE IF NOT EXISTS \"prototypes\"(prototype_id INTEGER,routeIDstart INTEGER,routeIDend INTEGER, nMatching INTEGER, PRIMARY KEY(prototype_id))")
-	
+    
     for route in prototypes.keys():
         if prototypes[route]!=[]:
             for i in prototypes[route]:
@@ -100,7 +100,7 @@
                     
     connection.commit()
     connection.close()
-	
+    
 def loadPrototypesFromSqlite(filename):
     """
     This function loads the prototype file in the database 
@@ -127,7 +127,7 @@
 
     connection.close()
     return prototypes,nMatching
-	
+    
 def writeLabelsToSqlite(labels, outputFilename):
     """ labels is a dictionary with  keys: routes, values: prototypes Ids
     """
@@ -135,7 +135,7 @@
     cursor = connection.cursor()
 
     cursor.execute("CREATE TABLE IF NOT EXISTS \"labels\"(object_id INTEGER,routeIDstart INTEGER,routeIDend INTEGER, prototype_id INTEGER, PRIMARY KEY(object_id))")
-	
+    
     for route in labels.keys():
         if labels[route]!=[]:
             for i in labels[route]:
@@ -144,7 +144,7 @@
                     
     connection.commit()
     connection.close()
-	
+    
 def loadLabelsFromSqlite(filename):
     labels = {}
 
@@ -174,7 +174,7 @@
     cursor = connection.cursor()
 
     cursor.execute("CREATE TABLE IF NOT EXISTS \"speedprototypes\"(spdprototype_id INTEGER,prototype_id INTEGER,routeID_start INTEGER, routeID_end INTEGER, nMatching INTEGER, PRIMARY KEY(spdprototype_id))")
-	
+    
     for route in prototypes.keys():
         if prototypes[route]!={}:
             for i in prototypes[route]:
@@ -184,7 +184,7 @@
                     
     connection.commit()
     connection.close()
-	
+    
 def loadSpeedPrototypeFromSqlite(filename):
     """
     This function loads the prototypes table in the database of name <filename>.
@@ -219,7 +219,7 @@
     cursor = connection.cursor()
 
     cursor.execute("CREATE TABLE IF NOT EXISTS \"routes\"(object_id INTEGER,routeIDstart INTEGER,routeIDend INTEGER, PRIMARY KEY(object_id))")
-	
+    
     for route in Routes.keys():
         if Routes[route]!=[]:
             for i in Routes[route]:
@@ -227,7 +227,7 @@
                     
     connection.commit()
     connection.close()
-	
+    
 def loadRoutesFromSqlite(filename):
     Routes = {}
 
@@ -247,14 +247,14 @@
         Routes[route].append(row[0])
 
     connection.close()
-    return Routes	
+    return Routes
 
 def setRoutes(filename, objects):
     connection = sqlite3.connect(filename)
     cursor = connection.cursor()
     for obj in objects:
         cursor.execute('update objects set startRouteID = {} where object_id = {}'.format(obj.startRouteID, obj.getNum()))
-        cursor.execute('update objects set endRouteID = {} where object_id = {}'.format(obj.endRouteID, obj.getNum()))	        
+        cursor.execute('update objects set endRouteID = {} where object_id = {}'.format(obj.endRouteID, obj.getNum()))        
     connection.commit()
     connection.close()
 
@@ -330,7 +330,7 @@
                 corner = 'top_left'
             elif trajectoryType == 'bbbottom':
                 corner = 'bottom_right'
-            queryStatement = 'SELECT object_id, frame_number, x_'+corner+', y_'+corner+' FROM '+tableName+' '+trajectoryIdQuery+'ORDER BY object_id, frame_number'
+            queryStatement = 'SELECT object_id, frame_number, x_'+corner+', y_'+corner+' FROM '+tableName+' '+idQuery+'ORDER BY object_id, frame_number'
             cursor.execute(queryStatement)
             logging.debug(queryStatement)
         else:
@@ -419,7 +419,7 @@
     connection.close()
     return objects
 
-def loadGroundTruthFromSqlite(filename, gtType, gtNumbers = None):
+def loadGroundTruthFromSqlite(filename, gtType = 'bb', gtNumbers = None):
     'Loads bounding box annotations (ground truth) from an SQLite '
     connection = sqlite3.connect(filename)
     gt = []
--- a/python/tests/moving.txt	Wed Dec 10 14:35:30 2014 -0500
+++ b/python/tests/moving.txt	Wed Dec 10 15:27:08 2014 -0500
@@ -160,3 +160,36 @@
 >>> o1.classifyUserTypeSpeedMotorized(1.5, np.median)
 >>> userTypeNames[o1.getUserType()]
 'pedestrian'
+
+>>> o1 = MovingObject.generate(Point(0.,0.), Point(1.,0.), TimeInterval(0,10))
+>>> gt1 = BBAnnotation(1, TimeInterval(0,10), MovingObject.generate(Point(0.2,0.6), Point(1.,0.), TimeInterval(0,10)), MovingObject.generate(Point(-0.2,-0.4), Point(1.,0.), TimeInterval(0,10)))
+>>> gt1.computeCentroidTrajectory()
+>>> computeClearMOT([gt1], [], 0.2, 0, 10)
+(None, 0.0, 11, 0, 0, 11)
+>>> computeClearMOT([], [o1], 0.2, 0, 10)
+(None, None, 0, 0, 11, 0)
+>>> computeClearMOT([gt1], [o1], 0.2, 0, 10) # doctest:+ELLIPSIS
+(0.0999..., 1.0, 0, 0, 0, 11)
+>>> computeClearMOT([gt1], [o1], 0.05, 0, 10)
+(None, -1.0, 11, 0, 11, 11)
+
+>>> o1 = MovingObject(1, TimeInterval(0,3), positions = Trajectory([range(4), [0.1, 0.1, 1.1, 1.1]]))
+>>> o2 = MovingObject(2, TimeInterval(0,3), positions = Trajectory([range(4), [0.9, 0.9, -0.1, -0.1]]))
+>>> gt1 = BBAnnotation(1, TimeInterval(0,3), MovingObject(positions = Trajectory([range(4), [0.]*4])), MovingObject(positions = Trajectory([range(4), [0.]*4])))
+>>> gt1.computeCentroidTrajectory()
+>>> gt2 = BBAnnotation(2, TimeInterval(0,3), MovingObject(positions = Trajectory([range(4), [1.]*4])), MovingObject(positions = Trajectory([range(4), [1.]*4])))
+>>> gt2.computeCentroidTrajectory()
+>>> computeClearMOT([gt1, gt2], [o1, o2], 0.2, 0, 3) # doctest:+ELLIPSIS
+(0.1..., 0.75, 0, 2, 0, 8)
+>>> computeClearMOT([gt2, gt1], [o2, o1], 0.2, 0, 3) # doctest:+ELLIPSIS
+(0.1..., 0.75, 0, 2, 0, 8)
+>>> computeClearMOT([gt1], [o1, o2], 0.2, 0, 3)
+(0.1, -0.25, 0, 1, 4, 4)
+>>> computeClearMOT([gt1], [o2, o1], 0.2, 0, 3) # symmetry
+(0.1, -0.25, 0, 1, 4, 4)
+>>> computeClearMOT([gt1, gt2], [o1], 0.2, 0, 3) # doctest:+ELLIPSIS
+(0.100..., 0.375, 4, 1, 0, 8)
+>>> computeClearMOT([gt2, gt1], [o1], 0.2, 0, 3) # doctest:+ELLIPSIS
+(0.100..., 0.375, 4, 1, 0, 8)
+>>> computeClearMOT([gt1, gt2], [o1, o2], 0.08, 0, 3)
+(None, -1.0, 8, 0, 8, 8)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/compute-clearmot.py	Wed Dec 10 15:27:08 2014 -0500
@@ -0,0 +1,39 @@
+#! /usr/bin/env python
+
+import sys, argparse
+from numpy import loadtxt
+import moving, storage
+
+# TODO: need to trim objects to same mask ?
+
+parser = argparse.ArgumentParser(description='The program computes the CLEAR MOT metrics between ground truth and tracker output (in Polytrack format)', epilog='''CLEAR MOT metrics information:
+Keni, Bernardin, and Stiefelhagen Rainer. "Evaluating multiple object tracking performance: the CLEAR MOT metrics." EURASIP Journal on Image and Video Processing 2008 (2008)
+
+Polytrack format: 
+JP. Jodoin\'s MSc thesis (in french)
+see examples on http://www.jpjodoin.com/urbantracker/dataset.html''', formatter_class=argparse.RawDescriptionHelpFormatter)
+parser.add_argument('-d', dest = 'trackerDatabaseFilename', help = 'name of the Sqlite database containing the tracker output', required = True)
+parser.add_argument('-g', dest = 'groundTruthDatabaseFilename', help = 'name of the Sqlite database containing the ground truth', required = True)
+parser.add_argument('-o', dest = 'homographyFilename', help = 'name of the filename for the homography (if tracking was done using the homography)')
+parser.add_argument('-m', dest = 'matchingDistance', help = 'matching distance between tracker and ground truth trajectories', required = True, type = float)
+parser.add_argument('-f', dest = 'firstInstant', help = 'first instant for measurement', required = True, type = int)
+parser.add_argument('-l', dest = 'lastInstant', help = 'last instant for measurement', required = True, type = int)
+args = parser.parse_args()
+
+if args.homographyFilename != None:
+    homography = loadtxt(args.homographyFilename)
+else:
+    homography = None
+
+objects = storage.loadTrajectoriesFromSqlite(args.trackerDatabaseFilename, 'object')
+annotations = storage.loadGroundTruthFromSqlite(args.groundTruthDatabaseFilename)
+for a in annotations:
+    a.computeCentroidTrajectory(homography)
+
+motp, mota, mt, mme, fpt, gt = moving.computeClearMOT(annotations, objects, args.matchingDistance, args.firstInstant, args.lastInstant)
+
+print 'MOTP: {}'.format(motp)
+print 'MOTA: {}'.format(mota)
+print 'Number of missed objects.frames: {}'.format(mt)
+print 'Number of mismatches: {}'.format(mme)
+print 'Number of false alarms.frames: {}'.format(fpt)
--- a/scripts/compute-homography.py	Wed Dec 10 14:35:30 2014 -0500
+++ b/scripts/compute-homography.py	Wed Dec 10 15:27:08 2014 -0500
@@ -17,7 +17,7 @@
 If providing video and world images, with a number of points to input
 and a ration to convert pixels to world distance unit (eg meters per pixel), 
 the images will be shown in turn and the user should click 
-in the same order the corresponding points in world and image spaces.''', formatter_class=argparse.RawDescriptionHelpFormatter,)
+in the same order the corresponding points in world and image spaces.''', formatter_class=argparse.RawDescriptionHelpFormatter)
 
 parser.add_argument('-p', dest = 'pointCorrespondencesFilename', help = 'name of the text file containing the point correspondences')
 parser.add_argument('-i', dest = 'videoFrameFilename', help = 'filename of the video frame')
--- a/tracking.cfg	Wed Dec 10 14:35:30 2014 -0500
+++ b/tracking.cfg	Wed Dec 10 15:27:08 2014 -0500
@@ -62,9 +62,9 @@
 # number of frames to compute velocities
 #nframes-velocity = 5
 # maximum number of iterations to stop feature tracking
-max-number-iterations = 30
+max-number-iterations = 20
 # minimum error to reach to stop feature tracking
-min-tracking-error = 0.01
+min-tracking-error = 0.3
 # minimum eigen value of a 2x2 normal matrix of optical flow equations
 min-feature-eig-threshold = 1e-4
 # minimum length of a feature (number of frames) to consider a feature for grouping