comparison trafficintelligence/moving.py @ 1110:6bbcd9433732

formatting and addition of one method to CurvilinearTrajectory
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Mon, 06 May 2019 16:16:28 -0400
parents 6e8ab471ebd4
children 345cd9cd62d8
comparison
equal deleted inserted replaced
1109:6e8ab471ebd4 1110:6bbcd9433732
271 def plotSegment(p1, p2, options = 'o', **kwargs): 271 def plotSegment(p1, p2, options = 'o', **kwargs):
272 plot([p1.x, p2.x], [p1.y, p2.y], options, **kwargs) 272 plot([p1.x, p2.x], [p1.y, p2.y], options, **kwargs)
273 273
274 def angle(self): 274 def angle(self):
275 return atan2(self.y, self.x) 275 return atan2(self.y, self.x)
276 276
277 def norm2Squared(self): 277 def norm2Squared(self):
278 '''2-norm distance (Euclidean distance)''' 278 '''2-norm distance (Euclidean distance)'''
279 return self.x**2+self.y**2 279 return self.x**2+self.y**2
280 280
281 def norm2(self): 281 def norm2(self):
282 '''2-norm distance (Euclidean distance)''' 282 '''2-norm distance (Euclidean distance)'''
283 return sqrt(self.norm2Squared()) 283 return sqrt(self.norm2Squared())
284 284
285 def norm1(self): 285 def norm1(self):
286 return abs(self.x)+abs(self.y) 286 return abs(self.x)+abs(self.y)
287 287
288 def normMax(self): 288 def normMax(self):
289 return max(abs(self.x),abs(self.y)) 289 return max(abs(self.x),abs(self.y))
290 290
291 def aslist(self): 291 def aslist(self):
292 return [self.x, self.y] 292 return [self.x, self.y]
344 return p1.x*p2.y-p1.y*p2.x 344 return p1.x*p2.y-p1.y*p2.x
345 345
346 @staticmethod 346 @staticmethod
347 def parallel(p1, p2): 347 def parallel(p1, p2):
348 return Point.cross(p1, p2) == 0. 348 return Point.cross(p1, p2) == 0.
349 349
350 @staticmethod 350 @staticmethod
351 def cosine(p1, p2): 351 def cosine(p1, p2):
352 return Point.dot(p1,p2)/(p1.norm2()*p2.norm2()) 352 return Point.dot(p1,p2)/(p1.norm2()*p2.norm2())
353 353
354 @staticmethod 354 @staticmethod
463 Y = (-(qx)*(p0y-p1y)-(qy*(p0y-p1y)**2)/(p0x-p1x)+p0x**2*(p0y-p1y)/(p0x-p1x)-p0x*p1x*(p0y-p1y)/(p0x-p1x)-p0y*(p0x-p1x))/(p1x-p0x-(p0y-p1y)**2/(p0x-p1x)) 463 Y = (-(qx)*(p0y-p1y)-(qy*(p0y-p1y)**2)/(p0x-p1x)+p0x**2*(p0y-p1y)/(p0x-p1x)-p0x*p1x*(p0y-p1y)/(p0x-p1x)-p0y*(p0x-p1x))/(p1x-p0x-(p0y-p1y)**2/(p0x-p1x))
464 X = (-Y*(p1y-p0y)+qx*(p1x-p0x)+qy*(p1y-p0y))/(p1x-p0x) 464 X = (-Y*(p1y-p0y)+qx*(p1x-p0x)+qy*(p1y-p0y))/(p1x-p0x)
465 except ZeroDivisionError: 465 except ZeroDivisionError:
466 print('Error: Division by zero in ppldb2p. Please report this error with the full traceback:') 466 print('Error: Division by zero in ppldb2p. Please report this error with the full traceback:')
467 print('qx={0}, qy={1}, p0x={2}, p0y={3}, p1x={4}, p1y={5}...'.format(qx, qy, p0x, p0y, p1x, p1y)) 467 print('qx={0}, qy={1}, p0x={2}, p0y={3}, p1x={4}, p1y={5}...'.format(qx, qy, p0x, p0y, p1x, p1y))
468 import pdb; pdb.set_trace() 468 import pdb;
469 return Point(X,Y) 469 pdb.set_trace()
470 return Point(X, Y)
470 471
471 def getSYfromXY(p, alignments, goodEnoughAlignmentDistance = 0.5): 472 def getSYfromXY(p, alignments, goodEnoughAlignmentDistance = 0.5):
472 ''' Snap a point p to its nearest subsegment of it's nearest alignment (from the list alignments). 473 ''' Snap a point p to its nearest subsegment of it's nearest alignment (from the list alignments).
473 A alignment is a list of points (class Point), most likely a trajectory. 474 A alignment is a list of points (class Point), most likely a trajectory.
474 475
539 return alignment[i-1] + normalizedV*d + orthoNormalizedV*y 540 return alignment[i-1] + normalizedV*d + orthoNormalizedV*y
540 else: 541 else:
541 print('Curvilinear point {} is past the end of the alignement'.format((s, y, alignmentNum))) 542 print('Curvilinear point {} is past the end of the alignement'.format((s, y, alignmentNum)))
542 return None 543 return None
543 544
544 545
545 class NormAngle(object): 546 class NormAngle(object):
546 '''Alternate encoding of a point, by its norm and orientation''' 547 '''Alternate encoding of a point, by its norm and orientation'''
547 548
548 def __init__(self, norm, angle): 549 def __init__(self, norm, angle):
549 self.norm = norm 550 self.norm = norm
550 self.angle = angle 551 self.angle = angle
551 552
552 @staticmethod 553 @staticmethod
553 def fromPoint(p): 554 def fromPoint(p):
554 norm = p.norm2() 555 norm = p.norm2()
555 if norm > 0: 556 if norm > 0:
556 angle = p.angle() 557 angle = p.angle()
596 return FlowVector(self.position.__mul__(alpha), self.velocity.__mul__(alpha)) 597 return FlowVector(self.position.__mul__(alpha), self.velocity.__mul__(alpha))
597 598
598 def plot(self, options = '', **kwargs): 599 def plot(self, options = '', **kwargs):
599 plot([self.position.x, self.position.x+self.velocity.x], [self.position.y, self.position.y+self.velocity.y], options, **kwargs) 600 plot([self.position.x, self.position.x+self.velocity.x], [self.position.y, self.position.y+self.velocity.y], options, **kwargs)
600 self.position.plot(options+'x', **kwargs) 601 self.position.plot(options+'x', **kwargs)
601 602
602 @staticmethod 603 @staticmethod
603 def similar(f1, f2, maxDistance2, maxDeltavelocity2): 604 def similar(f1, f2, maxDistance2, maxDeltavelocity2):
604 return (f1.position-f2.position).norm2Squared()<maxDistance2 and (f1.velocity-f2.velocity).norm2Squared()<maxDeltavelocity2 605 return (f1.position-f2.position).norm2Squared()<maxDistance2 and (f1.velocity-f2.velocity).norm2Squared()<maxDeltavelocity2
605 606
606 def intersection(p1, p2, p3, p4): 607 def intersection(p1, p2, p3, p4):
623 # from numpy.linalg import linalg 624 # from numpy.linalg import linalg
624 # A = matrix([[dp1.y, -dp1.x], 625 # A = matrix([[dp1.y, -dp1.x],
625 # [dp2.y, -dp2.x]]) 626 # [dp2.y, -dp2.x]])
626 # B = matrix([[dp1.y*p1.x-dp1.x*p1.y], 627 # B = matrix([[dp1.y*p1.x-dp1.x*p1.y],
627 # [dp2.y*p2.x-dp2.x*p2.y]]) 628 # [dp2.y*p2.x-dp2.x*p2.y]])
628 629
629 # if linalg.det(A) == 0: 630 # if linalg.det(A) == 0:
630 # return None 631 # return None
631 # else: 632 # else:
632 # intersection = linalg.solve(A,B) 633 # intersection = linalg.solve(A,B)
633 # return Point(intersection[0,0], intersection[1,0]) 634 # return Point(intersection[0,0], intersection[1,0])
653 inter = intersection(p1, p2, p3, p4) 654 inter = intersection(p1, p2, p3, p4)
654 if inter is not None and utils.inBetween(p3.x, p4.x, inter.x) and utils.inBetween(p3.y, p4.y, inter.y): 655 if inter is not None and utils.inBetween(p3.x, p4.x, inter.x) and utils.inBetween(p3.y, p4.y, inter.y):
655 return inter 656 return inter
656 else: 657 else:
657 return None 658 return None
658 659
659 660
660 class Trajectory(object): 661 class Trajectory(object):
661 '''Class for trajectories: temporal sequence of positions 662 '''Class for trajectories: temporal sequence of positions
662 663
663 The class is iterable''' 664 The class is iterable'''
790 def getYCoordinates(self): 791 def getYCoordinates(self):
791 return self.positions[1] 792 return self.positions[1]
792 793
793 def asArray(self): 794 def asArray(self):
794 return array(self.positions) 795 return array(self.positions)
795 796
796 def xBounds(self): 797 def xBounds(self):
797 # look for function that does min and max in one pass 798 # look for function that does min and max in one pass
798 return Interval(min(self.getXCoordinates()), max(self.getXCoordinates())) 799 return Interval(min(self.getXCoordinates()), max(self.getXCoordinates()))
799 800
800 def yBounds(self): 801 def yBounds(self):
801 # look for function that does min and max in one pass 802 # look for function that does min and max in one pass
802 return Interval(min(self.getYCoordinates()), max(self.getYCoordinates())) 803 return Interval(min(self.getYCoordinates()), max(self.getYCoordinates()))
803 804
804 def add(self, traj2): 805 def add(self, traj2):
805 '''Returns a new trajectory of the same length''' 806 '''Returns a new trajectory of the same length'''
806 if self.length() != traj2.length(): 807 if self.length() != traj2.length():
807 print('Trajectories of different lengths') 808 print('Trajectories of different lengths')
808 return None 809 return None
949 '''Returns a list of the indices at which the trajectory 950 '''Returns a list of the indices at which the trajectory
950 intersects with the line going through p1 and p2 951 intersects with the line going through p1 and p2
951 Returns an empty list if there is no crossing''' 952 Returns an empty list if there is no crossing'''
952 indices = [] 953 indices = []
953 intersections = [] 954 intersections = []
954 955
955 for i in range(self.length()-1): 956 for i in range(self.length()-1):
956 q1=self.__getitem__(i) 957 q1=self.__getitem__(i)
957 q2=self.__getitem__(i+1) 958 q2=self.__getitem__(i+1)
958 p = segmentLineIntersection(p1, p2, q1, q2) 959 p = segmentLineIntersection(p1, p2, q1, q2)
959 if p is not None: 960 if p is not None:
1056 self.positions = [S,Y] 1057 self.positions = [S,Y]
1057 if lanes is None or len(lanes) != self.length(): 1058 if lanes is None or len(lanes) != self.length():
1058 self.lanes = [None]*int(self.length()) 1059 self.lanes = [None]*int(self.length())
1059 else: 1060 else:
1060 self.lanes = lanes 1061 self.lanes = lanes
1061 1062
1062 @staticmethod 1063 @staticmethod
1063 def generate(s, v, nPoints, lane, y = 0): 1064 def generate(s, v, nPoints, lane, y = 0):
1064 '''s is initial position, v is velocity 1065 '''s is initial position, v is velocity
1065 0 in lateral coordinate by default 1066 0 in lateral coordinate by default
1066 TODO 2D velocity for lane change?''' 1067 TODO 2D velocity for lane change?'''
1132 raise TypeError("Invalid argument type.") 1133 raise TypeError("Invalid argument type.")
1133 #elif isinstance( key, slice ): 1134 #elif isinstance( key, slice ):
1134 1135
1135 def getSCoordinates(self): 1136 def getSCoordinates(self):
1136 return self.getXCoordinates() 1137 return self.getXCoordinates()
1137 1138
1138 def getLanes(self): 1139 def getLanes(self):
1139 return self.lanes 1140 return self.lanes
1140 1141
1141 def getSCoordAt(self, i): 1142 def getSCoordAt(self, i):
1142 return self.positions[0][i] 1143 return self.positions[0][i]
1161 self.lanes.append(lane) 1162 self.lanes.append(lane)
1162 1163
1163 def addPosition(self, p): 1164 def addPosition(self, p):
1164 'Adds position in the point format for curvilinear of list with 3 values' 1165 'Adds position in the point format for curvilinear of list with 3 values'
1165 self.addPositionSYL(p[0], p[1], p[2]) 1166 self.addPositionSYL(p[0], p[1], p[2])
1167
1168 def duplicateLastPosition(self):
1169 super(CurvilinearTrajectory, self).duplicateLastPosition()
1170 self.lanes.append(self.lanes[-1])
1166 1171
1167 def setPosition(self, i, s, y, lane): 1172 def setPosition(self, i, s, y, lane):
1168 self.setPositionXY(i, s, y) 1173 self.setPositionXY(i, s, y)
1169 if i < self.__len__(): 1174 if i < self.__len__():
1170 self.lanes[i] = lane 1175 self.lanes[i] = lane
1213 1218
1214 class CarClassifier: 1219 class CarClassifier:
1215 def predict(self, hog): 1220 def predict(self, hog):
1216 return userType2Num['car'] 1221 return userType2Num['car']
1217 carClassifier = CarClassifier() 1222 carClassifier = CarClassifier()
1218 1223
1219 class MovingObject(STObject, VideoFilenameAddable): 1224 class MovingObject(STObject, VideoFilenameAddable):
1220 '''Class for moving objects: a spatio-temporal object 1225 '''Class for moving objects: a spatio-temporal object
1221 with a trajectory and a geometry (constant volume over time) 1226 with a trajectory and a geometry (constant volume over time)
1222 and a usertype (e.g. road user) coded as a number (see userTypeNames) 1227 and a usertype (e.g. road user) coded as a number (see userTypeNames)
1223 ''' 1228 '''
1233 self.geometry = geometry 1238 self.geometry = geometry
1234 self.userType = userType 1239 self.userType = userType
1235 self.setNObjects(nObjects) # a feature has None for nObjects 1240 self.setNObjects(nObjects) # a feature has None for nObjects
1236 self.features = None 1241 self.features = None
1237 # compute bounding polygon from trajectory 1242 # compute bounding polygon from trajectory
1238 1243
1239 @staticmethod 1244 @staticmethod
1240 def croppedTimeInterval(obj, value, after = True): 1245 def croppedTimeInterval(obj, value, after = True):
1241 newTimeInterval = TimeInterval(obj.getFirstInstant(), min(value, obj.getLastInstant())) if after else TimeInterval(max(obj.getFirstInstant(), value), obj.getLastInstant()) 1246 newTimeInterval = TimeInterval(obj.getFirstInstant(), min(value, obj.getLastInstant())) if after else TimeInterval(max(obj.getFirstInstant(), value), obj.getLastInstant())
1242 if obj.positions is not None : 1247 if obj.positions is not None :
1243 newPositions = obj.positions[slice(newTimeInterval.first, newTimeInterval.last+1)] 1248 newPositions = obj.positions[slice(newTimeInterval.first, newTimeInterval.last+1)]
1517 return [(1-alpha)*p1[0]+alpha*p2[0], (1-alpha)*p1[1]+alpha*p2[1], lane] 1522 return [(1-alpha)*p1[0]+alpha*p2[0], (1-alpha)*p1[1]+alpha*p2[1], lane]
1518 else: 1523 else:
1519 print('Object {} does not exist at {}'.format(self.getNum(), t)) 1524 print('Object {} does not exist at {}'.format(self.getNum(), t))
1520 else: 1525 else:
1521 print('Object {} has no curvilinear positions'.format(self.getNum())) 1526 print('Object {} has no curvilinear positions'.format(self.getNum()))
1522 1527
1523 def setUserType(self, userType): 1528 def setUserType(self, userType):
1524 self.userType = userType 1529 self.userType = userType
1525 1530
1526 def getNObjects(self): 1531 def getNObjects(self):
1527 return self.nObjects 1532 return self.nObjects
1530 if nObjects is None or nObjects >= 1: 1535 if nObjects is None or nObjects >= 1:
1531 self.nObjects = nObjects 1536 self.nObjects = nObjects
1532 else: 1537 else:
1533 print('Number of objects represented by object {} must be greater or equal to 1 ({})'.format(self.getNum(), nObjects)) 1538 print('Number of objects represented by object {} must be greater or equal to 1 ({})'.format(self.getNum(), nObjects))
1534 self.nObjects = None 1539 self.nObjects = None
1535 1540
1536 def setFeatures(self, features, featuresOrdered = False): 1541 def setFeatures(self, features, featuresOrdered = False):
1537 '''Sets the features in the features field based on featureNumbers 1542 '''Sets the features in the features field based on featureNumbers
1538 if not all features are loaded from 0, one needs to renumber in a dict''' 1543 if not all features are loaded from 0, one needs to renumber in a dict'''
1539 if featuresOrdered: 1544 if featuresOrdered:
1540 tmp = features 1545 tmp = features
1589 '''Returns the 1-D acceleration from the 1-D speeds 1594 '''Returns the 1-D acceleration from the 1-D speeds
1590 Caution about previously filtered data''' 1595 Caution about previously filtered data'''
1591 if speeds is None: 1596 if speeds is None:
1592 speeds = self.getSpeeds(nInstantsIgnoredAtEnds) 1597 speeds = self.getSpeeds(nInstantsIgnoredAtEnds)
1593 return savgol_filter(speeds, window_length, polyorder, 1, delta, axis, mode, cval) 1598 return savgol_filter(speeds, window_length, polyorder, 1, delta, axis, mode, cval)
1594 1599
1595 def getSpeedIndicator(self): 1600 def getSpeedIndicator(self):
1596 from indicators import SeverityIndicator 1601 from indicators import SeverityIndicator
1597 return SeverityIndicator('Speed', {t:self.getVelocityAtInstant(t).norm2() for t in self.getTimeInterval()}) 1602 return SeverityIndicator('Speed', {t:self.getVelocityAtInstant(t).norm2() for t in self.getTimeInterval()})
1598 1603
1599 def getPositionAt(self, i): 1604 def getPositionAt(self, i):
1620 def getCurvilinearVelocityAtInstant(self, i): 1625 def getCurvilinearVelocityAtInstant(self, i):
1621 return self.curvilinearVelocities[i-self.getFirstInstant()] 1626 return self.curvilinearVelocities[i-self.getFirstInstant()]
1622 1627
1623 def getXCoordinates(self): 1628 def getXCoordinates(self):
1624 return self.positions.getXCoordinates() 1629 return self.positions.getXCoordinates()
1625 1630
1626 def getYCoordinates(self): 1631 def getYCoordinates(self):
1627 return self.positions.getYCoordinates() 1632 return self.positions.getYCoordinates()
1628 1633
1629 def plot(self, options = '', withOrigin = False, timeStep = 1, withFeatures = False, withIds = False, **kwargs): 1634 def plot(self, options = '', withOrigin = False, timeStep = 1, withFeatures = False, withIds = False, **kwargs):
1630 if withIds: 1635 if withIds:
1631 objNum = self.getNum() 1636 objNum = self.getNum()
1632 else: 1637 else:
1633 objNum = None 1638 objNum = None
1688 else: 1693 else:
1689 instant2 = _instant2 1694 instant2 = _instant2
1690 positions1 = [f.getPositionAtInstant(instant1).astuple() for f in obj1.features if f.existsAtInstant(instant1)] 1695 positions1 = [f.getPositionAtInstant(instant1).astuple() for f in obj1.features if f.existsAtInstant(instant1)]
1691 positions2 = [f.getPositionAtInstant(instant2).astuple() for f in obj2.features if f.existsAtInstant(instant2)] 1696 positions2 = [f.getPositionAtInstant(instant2).astuple() for f in obj2.features if f.existsAtInstant(instant2)]
1692 return cdist(positions1, positions2, metric = 'euclidean') 1697 return cdist(positions1, positions2, metric = 'euclidean')
1693 1698
1694 @staticmethod 1699 @staticmethod
1695 def minDistance(obj1, obj2, instant1, instant2 = None): 1700 def minDistance(obj1, obj2, instant1, instant2 = None):
1696 return MovingObject.distances(obj1, obj2, instant1, instant2).min() 1701 return MovingObject.distances(obj1, obj2, instant1, instant2).min()
1697 1702
1698 @staticmethod 1703 @staticmethod
1712 tMaxFeatures = t 1717 tMaxFeatures = t
1713 return MovingObject.maxDistance(self, self, tMaxFeatures) 1718 return MovingObject.maxDistance(self, self, tMaxFeatures)
1714 else: 1719 else:
1715 print('Load features to compute a maximum size') 1720 print('Load features to compute a maximum size')
1716 return None 1721 return None
1717 1722
1718 def setRoutes(self, startRouteID, endRouteID): 1723 def setRoutes(self, startRouteID, endRouteID):
1719 self.startRouteID = startRouteID 1724 self.startRouteID = startRouteID
1720 self.endRouteID = endRouteID 1725 self.endRouteID = endRouteID
1721 1726
1722 def getInstantsCrossingLane(self, p1, p2): 1727 def getInstantsCrossingLane(self, p1, p2):
1723 '''Returns the instant(s) 1728 '''Returns the instant(s)
1724 at which the object passes from one side of the segment to the other 1729 at which the object passes from one side of the segment to the other
1725 empty list if there is no crossing''' 1730 empty list if there is no crossing'''
1726 indices, intersections = self.positions.getIntersections(p1, p2) 1731 indices, intersections = self.positions.getIntersections(p1, p2)
1732 self.prototypeSimilarities = [] 1737 self.prototypeSimilarities = []
1733 for proto in prototypes: 1738 for proto in prototypes:
1734 lcss.similarities(proto.getMovingObject().getPositions().asArray().T, self.getPositions().asArray().T) 1739 lcss.similarities(proto.getMovingObject().getPositions().asArray().T, self.getPositions().asArray().T)
1735 similarities = lcss.similarityTable[-1, :-1].astype(float) 1740 similarities = lcss.similarityTable[-1, :-1].astype(float)
1736 self.prototypeSimilarities.append(similarities/minimum(arange(1., len(similarities)+1), proto.getMovingObject().length()*ones(len(similarities)))) 1741 self.prototypeSimilarities.append(similarities/minimum(arange(1., len(similarities)+1), proto.getMovingObject().length()*ones(len(similarities))))
1737 1742
1738 @staticmethod 1743 @staticmethod
1739 def computePET(obj1, obj2, collisionDistanceThreshold): 1744 def computePET(obj1, obj2, collisionDistanceThreshold):
1740 '''Post-encroachment time based on distance threshold 1745 '''Post-encroachment time based on distance threshold
1741 1746
1742 Returns the smallest time difference when the object positions are within collisionDistanceThreshold 1747 Returns the smallest time difference when the object positions are within collisionDistanceThreshold
1809 ### 1814 ###
1810 def classifyUserTypeSpeedMotorized(self, threshold, aggregationFunc = median, nInstantsIgnoredAtEnds = 0): 1815 def classifyUserTypeSpeedMotorized(self, threshold, aggregationFunc = median, nInstantsIgnoredAtEnds = 0):
1811 '''Classifies slow and fast road users 1816 '''Classifies slow and fast road users
1812 slow: non-motorized -> pedestrians 1817 slow: non-motorized -> pedestrians
1813 fast: motorized -> cars 1818 fast: motorized -> cars
1814 1819
1815 aggregationFunc can be any function that can be applied to a vector of speeds, including percentile: 1820 aggregationFunc can be any function that can be applied to a vector of speeds, including percentile:
1816 aggregationFunc = lambda x: percentile(x, percentileFactor) # where percentileFactor is 85 for 85th percentile''' 1821 aggregationFunc = lambda x: percentile(x, percentileFactor) # where percentileFactor is 85 for 85th percentile'''
1817 speeds = self.getSpeeds(nInstantsIgnoredAtEnds) 1822 speeds = self.getSpeeds(nInstantsIgnoredAtEnds)
1818 if aggregationFunc(speeds) >= threshold: 1823 if aggregationFunc(speeds) >= threshold:
1819 self.setUserType(userType2Num['car']) 1824 self.setUserType(userType2Num['car'])
1968 return '{} {} {}'.format(self.filename, self.num, self.trajectoryType) 1973 return '{} {} {}'.format(self.filename, self.num, self.trajectoryType)
1969 def __eq__(self, p2): 1974 def __eq__(self, p2):
1970 return self.filename == p2.filename and self.num == p2.num and self.trajectoryType == p2.trajectoryType 1975 return self.filename == p2.filename and self.num == p2.num and self.trajectoryType == p2.trajectoryType
1971 def __hash__(self): 1976 def __hash__(self):
1972 return hash((self.filename, self.num, self.trajectoryType)) 1977 return hash((self.filename, self.num, self.trajectoryType))
1973 1978
1974 ################## 1979 ##################
1975 # Annotations 1980 # Annotations
1976 ################## 1981 ##################
1977 1982
1978 class BBMovingObject(MovingObject): 1983 class BBMovingObject(MovingObject):
2098 print('{} '.format(t)+', '.join(['{} {}'.format(k.getNum(), v.getNum()) for k,v in matches.items()])) 2103 print('{} '.format(t)+', '.join(['{} {}'.format(k.getNum(), v.getNum()) for k,v in matches.items()]))
2099 if returnMatches: 2104 if returnMatches:
2100 for a,o in matches.items(): 2105 for a,o in matches.items():
2101 gtMatches[a.getNum()][t] = o.getNum() 2106 gtMatches[a.getNum()][t] = o.getNum()
2102 toMatches[o.getNum()][t] = a.getNum() 2107 toMatches[o.getNum()][t] = a.getNum()
2103 2108
2104 # compute metrics elements 2109 # compute metrics elements
2105 ct += len(matches) 2110 ct += len(matches)
2106 mt += nGTs-len(matches) 2111 mt += nGTs-len(matches)
2107 fpt += nTOs-len(matches) 2112 fpt += nTOs-len(matches)
2108 gt += nGTs 2113 gt += nGTs
2121 if debug: 2126 if debug:
2122 for mm in set(mismatches): 2127 for mm in set(mismatches):
2123 print('{} {}'.format(type(mm), mm.getNum())) 2128 print('{} {}'.format(type(mm), mm.getNum()))
2124 # some object mismatches may appear twice 2129 # some object mismatches may appear twice
2125 mme += len(set(mismatches)) 2130 mme += len(set(mismatches))
2126 2131
2127 if ct > 0: 2132 if ct > 0:
2128 motp = dist/ct 2133 motp = dist/ct
2129 else: 2134 else:
2130 motp = None 2135 motp = None
2131 if gt > 0: 2136 if gt > 0: