Mercurial > hg > nsaunier > traffic-intelligence
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: |
