comparison trafficintelligence/moving.py @ 1096:9a32d63bae3f

update from Lionel Nebot Janvier
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Fri, 15 Feb 2019 14:35:56 -0500
parents c96388c696ac
children b3f8b26ee838
comparison
equal deleted inserted replaced
1095:e53c6e87bb3f 1096:9a32d63bae3f
151 # with methods to create intersection, unions... 151 # with methods to create intersection, unions...
152 # ''' 152 # '''
153 # We will use the polygon class of Shapely 153 # We will use the polygon class of Shapely
154 154
155 class STObject(object): 155 class STObject(object):
156 '''Class for spatio-temporal object, i.e. with temporal and spatial existence 156 '''Class for spatio-temporal object, i.e. with temporal and spatial existence
157 (time interval and bounding polygon for positions (e.g. rectangle)). 157 (time interval and bounding polygon for positions (e.g. rectangle)).
158 158
159 It may not mean that the object is defined 159 It may not mean that the object is defined
160 for all time instants within the time interval''' 160 for all time instants within the time interval'''
161 161
162 def __init__(self, num = None, timeInterval = None, boundingPolygon = None): 162 def __init__(self, num = None, timeInterval = None, boundingPolygon = None):
163 self.num = num 163 self.num = num
164 self.timeInterval = timeInterval 164 self.timeInterval = timeInterval
444 print('qx={0}, qy={1}, p0x={2}, p0y={3}, p1x={4}, p1y={5}...'.format(qx, qy, p0x, p0y, p1x, p1y)) 444 print('qx={0}, qy={1}, p0x={2}, p0y={3}, p1x={4}, p1y={5}...'.format(qx, qy, p0x, p0y, p1x, p1y))
445 import pdb; pdb.set_trace() 445 import pdb; pdb.set_trace()
446 return Point(X,Y) 446 return Point(X,Y)
447 447
448 def getSYfromXY(p, alignments, goodEnoughAlignmentDistance = 0.5): 448 def getSYfromXY(p, alignments, goodEnoughAlignmentDistance = 0.5):
449 ''' Snap a point p to its nearest subsegment of it's nearest alignment (from the list alignments). 449 ''' Snap a point p to its nearest subsegment of it's nearest alignment (from the list alignments).
450 A alignment is a list of points (class Point), most likely a trajectory. 450 A alignment is a list of points (class Point), most likely a trajectory.
451 451
452 Output: 452 Output:
453 ======= 453 =======
454 [alignment index, 454 [alignment index,
455 subsegment leading point index, 455 subsegment leading point index,
456 snapped point, 456 snapped point,
469 closestPoint = ppldb2p(p.x,p.y,alignments[alignmentIdx][alignment_p][0],alignments[alignmentIdx][alignment_p][1],alignments[alignmentIdx][alignment_p+1][0],alignments[alignmentIdx][alignment_p+1][1]) 469 closestPoint = ppldb2p(p.x,p.y,alignments[alignmentIdx][alignment_p][0],alignments[alignmentIdx][alignment_p][1],alignments[alignmentIdx][alignment_p+1][0],alignments[alignmentIdx][alignment_p+1][1])
470 if closestPoint is None: 470 if closestPoint is None:
471 print('Error: Alignment {0}, segment {1} has identical bounds and therefore is not a vector. Projection cannot continue.'.format(alignmentIdx, alignment_p)) 471 print('Error: Alignment {0}, segment {1} has identical bounds and therefore is not a vector. Projection cannot continue.'.format(alignmentIdx, alignment_p))
472 return None 472 return None
473 # check if the projected point is in between the current segment of the alignment bounds 473 # check if the projected point is in between the current segment of the alignment bounds
474 if utils.inBetween(alignments[alignmentIdx][alignment_p][0], alignments[alignmentIdx][alignment_p+1][0], closestPoint.x) and utils.inBetween(alignments[alignmentIdx][alignment_p][1], alignments[alignmentIdx][alignment_p+1][1], closestPoint.y): 474 if utils.inBetween(alignments[alignmentIdx][alignment_p][0], alignments[alignmentIdx][alignment_p+1][0], closestPoint.x) and utils.inBetween(alignments[alignmentIdx][alignment_p][1], alignments[alignmentIdx][alignment_p+1][1], closestPoint.y):
475 offsetY = Point.distanceNorm2(closestPoint, p) 475 offsetY = Point.distanceNorm2(closestPoint, p)
476 if offsetY < minOffsetY: 476 if offsetY < minOffsetY:
477 minOffsetY = offsetY 477 minOffsetY = offsetY
478 snappedAlignmentIdx = alignmentIdx 478 snappedAlignmentIdx = alignmentIdx
479 snappedAlignmentLeadingPoint = alignment_p 479 snappedAlignmentLeadingPoint = alignment_p
480 snappedPoint = Point(closestPoint.x, closestPoint.y) 480 snappedPoint = Point(closestPoint.x, closestPoint.y)
481 #Jump loop if significantly close 481 #Jump loop if significantly close
482 if offsetY < goodEnoughAlignmentDistance: 482 if offsetY < goodEnoughAlignmentDistance:
483 break 483 break
484 484
485 #Get sub-segment distance 485 #Get sub-segment distance
486 if minOffsetY != float('inf'): 486 if minOffsetY != float('inf'):
487 subsegmentDistance = Point.distanceNorm2(snappedPoint, alignments[snappedAlignmentIdx][snappedAlignmentLeadingPoint]) 487 subsegmentDistance = Point.distanceNorm2(snappedPoint, alignments[snappedAlignmentIdx][snappedAlignmentLeadingPoint])
495 else: 495 else:
496 print('Offset for point {} is infinite (check with prepareAlignments if some alignment segments are aligned with axes)'.format(p)) 496 print('Offset for point {} is infinite (check with prepareAlignments if some alignment segments are aligned with axes)'.format(p))
497 return None 497 return None
498 498
499 def getXYfromSY(s, y, alignmentNum, alignments): 499 def getXYfromSY(s, y, alignmentNum, alignments):
500 ''' Find X,Y coordinate from S,Y data. 500 ''' Find X,Y coordinate from S,Y data.
501 if mode = 0 : return Snapped X,Y 501 if mode = 0 : return Snapped X,Y
502 if mode !=0 : return Real X,Y 502 if mode !=0 : return Real X,Y
503 ''' 503 '''
504 alignment = alignments[alignmentNum] 504 alignment = alignments[alignmentNum]
505 i = 1 505 i = 1
506 while s > alignment.getCumulativeDistance(i) and i < len(alignment): 506 while s > alignment.getCumulativeDistance(i) and i < len(alignment):
507 i += 1 507 i += 1
508 if i < len(alignment): 508 if i < len(alignment):
593 else: 593 else:
594 ua = (dp34.x*(p1.y-p3.y)-dp34.y*(p1.x-p3.x))/det 594 ua = (dp34.x*(p1.y-p3.y)-dp34.y*(p1.x-p3.x))/det
595 return p1+dp12.__mul__(ua) 595 return p1+dp12.__mul__(ua)
596 596
597 # def intersection(p1, p2, dp1, dp2): 597 # def intersection(p1, p2, dp1, dp2):
598 # '''Returns the intersection point between the two lines 598 # '''Returns the intersection point between the two lines
599 # defined by the respective vectors (dp) and origin points (p)''' 599 # defined by the respective vectors (dp) and origin points (p)'''
600 # from numpy import matrix 600 # from numpy import matrix
601 # from numpy.linalg import linalg 601 # from numpy.linalg import linalg
602 # A = matrix([[dp1.y, -dp1.x], 602 # A = matrix([[dp1.y, -dp1.x],
603 # [dp2.y, -dp2.x]]) 603 # [dp2.y, -dp2.x]])
615 615
616 if (Interval.intersection(Interval(p1.x,p2.x,True), Interval(p3.x,p4.x,True)).empty()) or (Interval.intersection(Interval(p1.y,p2.y,True), Interval(p3.y,p4.y,True)).empty()): 616 if (Interval.intersection(Interval(p1.x,p2.x,True), Interval(p3.x,p4.x,True)).empty()) or (Interval.intersection(Interval(p1.y,p2.y,True), Interval(p3.y,p4.y,True)).empty()):
617 return None 617 return None
618 else: 618 else:
619 inter = intersection(p1, p2, p3, p4) 619 inter = intersection(p1, p2, p3, p4)
620 if (inter is not None 620 if (inter is not None
621 and utils.inBetween(p1.x, p2.x, inter.x) 621 and utils.inBetween(p1.x, p2.x, inter.x)
622 and utils.inBetween(p3.x, p4.x, inter.x) 622 and utils.inBetween(p3.x, p4.x, inter.x)
623 and utils.inBetween(p1.y, p2.y, inter.y) 623 and utils.inBetween(p1.y, p2.y, inter.y)
624 and utils.inBetween(p3.y, p4.y, inter.y)): 624 and utils.inBetween(p3.y, p4.y, inter.y)):
625 return inter 625 return inter
861 return self.cumulativeDistances[i] 861 return self.cumulativeDistances[i]
862 else: 862 else:
863 print('Index {} beyond trajectory length {}'.format(i, self.length())) 863 print('Index {} beyond trajectory length {}'.format(i, self.length()))
864 864
865 def getMaxDistance(self, metric): 865 def getMaxDistance(self, metric):
866 'Returns the maximum distance between points in the trajectory' 866 'Returns the maximum distance between points in the trajectory'
867 positions = self.getPositions().asArray().T 867 positions = self.getPositions().asArray().T
868 return cdist(positions, positions, metric = metric).max() 868 return cdist(positions, positions, metric = metric).max()
869 869
870 def getClosestPoint(self, p1, maxDist2 = None): 870 def getClosestPoint(self, p1, maxDist2 = None):
871 '''Returns the instant of the closest position in trajectory to p1 (and the point) 871 '''Returns the instant of the closest position in trajectory to p1 (and the point)
883 return None 883 return None
884 else: 884 else:
885 return i 885 return i
886 886
887 def similarOrientation(self, refDirection, cosineThreshold, minProportion = 0.5): 887 def similarOrientation(self, refDirection, cosineThreshold, minProportion = 0.5):
888 '''Indicates whether the minProportion (<=1.) (eg half) of the trajectory elements (vectors for velocity) 888 '''Indicates whether the minProportion (<=1.) (eg half) of the trajectory elements (vectors for velocity)
889 have a cosine with refDirection is smaller than cosineThreshold''' 889 have a cosine with refDirection is smaller than cosineThreshold'''
890 count = 0 890 count = 0
891 lengthThreshold = float(self.length())*minProportion 891 lengthThreshold = float(self.length())*minProportion
892 for p in self: 892 for p in self:
893 if p.similarOrientation(refDirection, cosineThreshold): 893 if p.similarOrientation(refDirection, cosineThreshold):
900 return self.getCumulativeDistance(self.length()-1)/float(straightDistance) 900 return self.getCumulativeDistance(self.length()-1)/float(straightDistance)
901 else: 901 else:
902 return None 902 return None
903 903
904 def getIntersections(self, p1, p2): 904 def getIntersections(self, p1, p2):
905 '''Returns a list of the indices at which the trajectory 905 '''Returns a list of the indices at which the trajectory
906 intersects with the segment of extremities p1 and p2 906 intersects with the segment of extremities p1 and p2
907 Returns an empty list if there is no crossing''' 907 Returns an empty list if there is no crossing'''
908 indices = [] 908 indices = []
909 intersections = [] 909 intersections = []
910 910
911 for i in range(self.length()-1): 911 for i in range(self.length()-1):
922 indices.append(i+ratio) 922 indices.append(i+ratio)
923 intersections.append(p) 923 intersections.append(p)
924 return indices, intersections 924 return indices, intersections
925 925
926 def getLineIntersections(self, p1, p2): 926 def getLineIntersections(self, p1, p2):
927 '''Returns a list of the indices at which the trajectory 927 '''Returns a list of the indices at which the trajectory
928 intersects with the line going through p1 and p2 928 intersects with the line going through p1 and p2
929 Returns an empty list if there is no crossing''' 929 Returns an empty list if there is no crossing'''
930 indices = [] 930 indices = []
931 intersections = [] 931 intersections = []
932 932
933 for i in range(self.length()-1): 933 for i in range(self.length()-1):
970 970
971 def getTrajectoryInPolygon(self, polygon, t2 = None): 971 def getTrajectoryInPolygon(self, polygon, t2 = None):
972 '''Returns the trajectory built with the set of points inside the (shapely) polygon 972 '''Returns the trajectory built with the set of points inside the (shapely) polygon
973 The polygon could be a prepared polygon (faster) from prepared.prep 973 The polygon could be a prepared polygon (faster) from prepared.prep
974 974
975 t2 is another trajectory (could be velocities) 975 t2 is another trajectory (could be velocities)
976 which is filtered based on the first (self) trajectory''' 976 which is filtered based on the first (self) trajectory'''
977 traj = Trajectory() 977 traj = Trajectory()
978 inPolygon = [] 978 inPolygon = []
979 for x, y in zip(self.positions[0], self.positions[1]): 979 for x, y in zip(self.positions[0], self.positions[1]):
980 inPolygon.append(polygon.contains(shapelyPoint(x, y))) 980 inPolygon.append(polygon.contains(shapelyPoint(x, y)))
1019 1019
1020 class CurvilinearTrajectory(Trajectory): 1020 class CurvilinearTrajectory(Trajectory):
1021 '''Sub class of trajectory for trajectories with curvilinear coordinates and lane assignements 1021 '''Sub class of trajectory for trajectories with curvilinear coordinates and lane assignements
1022 longitudinal coordinate is stored as first coordinate (exterior name S) 1022 longitudinal coordinate is stored as first coordinate (exterior name S)
1023 lateral coordinate is stored as second coordinate 1023 lateral coordinate is stored as second coordinate
1024 the third "lane" coordinate is for an alignment id, 1024 the third "lane" coordinate is for an alignment id,
1025 whether explicit for a list/dict of alignments, 1025 whether explicit for a list/dict of alignments,
1026 or implicit for a road with lane numbers''' 1026 or implicit for a road with lane numbers'''
1027 1027
1028 def __init__(self, S = None, Y = None, lanes = None): 1028 def __init__(self, S = None, Y = None, lanes = None):
1029 if S is None or Y is None or len(S) != len(Y): 1029 if S is None or Y is None or len(S) != len(Y):
1030 self.positions = [[],[]] 1030 self.positions = [[],[]]
1082 print('Warning: trajectory at point {} {} has alignment errors (alignment snapping)\nCurvilinear trajectory could not be computed'.format(i, t[i])) 1082 print('Warning: trajectory at point {} {} has alignment errors (alignment snapping)\nCurvilinear trajectory could not be computed'.format(i, t[i]))
1083 else: 1083 else:
1084 [align, alignPoint, snappedPoint, subsegmentDistance, S, Y] = result 1084 [align, alignPoint, snappedPoint, subsegmentDistance, S, Y] = result
1085 curvilinearPositions.addPositionSYL(S, Y, align) 1085 curvilinearPositions.addPositionSYL(S, Y, align)
1086 1086
1087 ## Go back through points and correct lane 1087 ## Go back through points and correct lane
1088 #Run through objects looking for outlier point 1088 #Run through objects looking for outlier point
1089 smoothed_lanes = utils.filterCategoricalMovingWindow(curvilinearPositions.getLanes(), halfWidth) 1089 smoothed_lanes = utils.filterCategoricalMovingWindow(curvilinearPositions.getLanes(), halfWidth)
1090 ## Recalculate projected point to new lane 1090 ## Recalculate projected point to new lane
1091 lanes = curvilinearPositions.getLanes() 1091 lanes = curvilinearPositions.getLanes()
1092 if(lanes != smoothed_lanes): 1092 if(lanes != smoothed_lanes):
1101 else: 1101 else:
1102 [align, alignPoint, snappedPoint, subsegmentDistance, S, Y] = result 1102 [align, alignPoint, snappedPoint, subsegmentDistance, S, Y] = result
1103 curvilinearPositions.setPosition(i, S, Y, align) 1103 curvilinearPositions.setPosition(i, S, Y, align)
1104 return curvilinearPositions 1104 return curvilinearPositions
1105 1105
1106 def __getitem__(self,i): 1106 def __getitem__(self,i):
1107 if isinstance(i, int): 1107 if isinstance(i, int):
1108 return [self.positions[0][i], self.positions[1][i], self.lanes[i]] 1108 return [self.positions[0][i], self.positions[1][i], self.lanes[i]]
1109 else: 1109 else:
1110 raise TypeError("Invalid argument type.") 1110 raise TypeError("Invalid argument type.")
1111 #elif isinstance( key, slice ): 1111 #elif isinstance( key, slice ):
1139 if doubleLastPosition and self.length() > 1: 1139 if doubleLastPosition and self.length() > 1:
1140 diff.addPosition(diff[-1]) 1140 diff.addPosition(diff[-1])
1141 return diff 1141 return diff
1142 1142
1143 def getIntersections(self, S1, lane = None): 1143 def getIntersections(self, S1, lane = None):
1144 '''Returns a list of the indices at which the trajectory 1144 '''Returns a list of the indices at which the trajectory
1145 goes past the curvilinear coordinate S1 1145 goes past the curvilinear coordinate S1
1146 (in provided lane if lane is not None) 1146 (in provided lane if lane is not None)
1147 Returns an empty list if there is no crossing''' 1147 Returns an empty list if there is no crossing'''
1148 indices = [] 1148 indices = []
1149 for i in range(self.length()-1): 1149 for i in range(self.length()-1):
1171 def predict(self, hog): 1171 def predict(self, hog):
1172 return userType2Num['car'] 1172 return userType2Num['car']
1173 carClassifier = CarClassifier() 1173 carClassifier = CarClassifier()
1174 1174
1175 class MovingObject(STObject, VideoFilenameAddable): 1175 class MovingObject(STObject, VideoFilenameAddable):
1176 '''Class for moving objects: a spatio-temporal object 1176 '''Class for moving objects: a spatio-temporal object
1177 with a trajectory and a geometry (constant volume over time) 1177 with a trajectory and a geometry (constant volume over time)
1178 and a usertype (e.g. road user) coded as a number (see userTypeNames) 1178 and a usertype (e.g. road user) coded as a number (see userTypeNames)
1179 ''' 1179 '''
1180 1180
1181 def __init__(self, num = None, timeInterval = None, positions = None, velocities = None, geometry = None, userType = userType2Num['unknown'], nObjects = None): 1181 def __init__(self, num = None, timeInterval = None, positions = None, velocities = None, geometry = None, userType = userType2Num['unknown'], nObjects = None):
1182 super(MovingObject, self).__init__(num, timeInterval) 1182 super(MovingObject, self).__init__(num, timeInterval)
1239 1239
1240 def updatePositions(self): 1240 def updatePositions(self):
1241 inter, self.positions, self.velocities = MovingObject.aggregateTrajectories(self.features, self.getTimeInterval()) 1241 inter, self.positions, self.velocities = MovingObject.aggregateTrajectories(self.features, self.getTimeInterval())
1242 1242
1243 def updateCurvilinearPositions(self, method, changeOfAlignment, nextAlignment_idx, timeStep = None, time = None, 1243 def updateCurvilinearPositions(self, method, changeOfAlignment, nextAlignment_idx, timeStep = None, time = None,
1244 leaderVehicleCurvilinearPositionAtPrecedentTime = None, previousAlignmentId = None, 1244 leaderVehicle = None, previousAlignmentId = None,
1245 maxSpeed = None, acceleration = None, seed = None, delta = None): 1245 maxSpeed = None, acceleration = None):
1246 import math
1247 1246
1248 if method == 'newell': 1247 if method == 'newell':
1249 self.curvilinearPositions.addPositionSYL(leaderVehicleCurvilinearPositionAtPrecedentTime - self.dn, 0, nextAlignment_idx) 1248 if time <= self.timeInterval[0] < time + timeStep:
1250 if changeOfAlignment: 1249 # #si t < instant de creation du vehicule, la position vaut l'espacement dn entre les deux vehicules
1251 self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep, 1250 if leaderVehicle is None:
1252 (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-1][1])/timeStep, 1251 self.curvilinearPositions.addPositionSYL(
1253 (previousAlignmentId, nextAlignment_idx)) 1252 -self.dn,
1253 0,
1254 nextAlignment_idx)
1255 else:
1256 self.curvilinearPositions.addPositionSYL(
1257 leaderVehicle.curvilinearPositions[0][0] - self.dn,
1258 0,
1259 nextAlignment_idx)
1254 else: 1260 else:
1255 self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep, 1261 if leaderVehicle is None:
1256 (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-1][1])/timeStep, 1262 self.curvilinearPositions.addPositionSYL(self.curvilinearPositions[-1][0]+self.desiredSpeed*timeStep, 0, nextAlignment_idx)
1257 None) 1263 else:
1258 elif method == 'constantSpeed': 1264 if time > self.reactionTime:
1259 random.seed(seed) 1265 previousVehicleCurvilinearPositionAtPrecedentTime = \
1260 self.curvilinearPositions.addPositionSYL(self.curvilinearPositions[time-1][0] + self.desiredSpeed * timeStep, 1266 leaderVehicle.curvilinearPositions[-int(round(self.reactionTime))][0] # t-v.reactionTime
1261 0, 1267 else:
1262 nextAlignment_idx) 1268 previousVehicleCurvilinearPositionAtPrecedentTime = \
1269 leaderVehicle.curvilinearPositions[0][0]
1270
1271 self.curvilinearPositions.addPositionSYL(previousVehicleCurvilinearPositionAtPrecedentTime - self.dn, 0, nextAlignment_idx)
1272
1273 #mise ajour des vitesses
1274 if changeOfAlignment:
1275 self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep,
1276 (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-2][1])/timeStep,
1277 (previousAlignmentId, nextAlignment_idx))
1278 else:
1279 self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep,
1280 (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-2][1])/timeStep,
1281 None)
1263 @staticmethod 1282 @staticmethod
1264 def concatenate(obj1, obj2, num = None, newFeatureNum = None, computePositions = False): 1283 def concatenate(obj1, obj2, num = None, newFeatureNum = None, computePositions = False):
1265 '''Concatenates two objects, whether overlapping temporally or not 1284 '''Concatenates two objects, whether overlapping temporally or not
1266 1285
1267 Positions will be recomputed only if computePositions is True 1286 Positions will be recomputed only if computePositions is True
1447 def getNLongestFeatures(self, nFeatures = 1): 1466 def getNLongestFeatures(self, nFeatures = 1):
1448 if self.features is None: 1467 if self.features is None:
1449 return [] 1468 return []
1450 else: 1469 else:
1451 tmp = utils.sortByLength(self.getFeatures(), reverse = True) 1470 tmp = utils.sortByLength(self.getFeatures(), reverse = True)
1452 return tmp[:min(len(tmp), nFeatures)] 1471 return tmp[:min(len(tmp), nFeatures)]
1453 1472
1454 def getFeatureNumbersOverTime(self): 1473 def getFeatureNumbersOverTime(self):
1455 '''Returns the number of features at each instant 1474 '''Returns the number of features at each instant
1456 dict instant -> number of features''' 1475 dict instant -> number of features'''
1457 if self.hasFeatures(): 1476 if self.hasFeatures():
1458 featureNumbers = {} 1477 featureNumbers = {}
1556 else: 1575 else:
1557 return int(commonTimeInterval.length()), None, None 1576 return int(commonTimeInterval.length()), None, None
1558 1577
1559 @staticmethod 1578 @staticmethod
1560 def distances(obj1, obj2, instant1, _instant2 = None): 1579 def distances(obj1, obj2, instant1, _instant2 = None):
1561 '''Returns the distances between all features of the 2 objects 1580 '''Returns the distances between all features of the 2 objects
1562 at the same instant instant1 1581 at the same instant instant1
1563 or at instant1 and instant2''' 1582 or at instant1 and instant2'''
1564 if _instant2 is None: 1583 if _instant2 is None:
1565 instant2 = instant1 1584 instant2 = instant1
1566 else: 1585 else:
1636 return smallPets[petIdx], obj1.getFirstInstant()+distanceIndices[0], obj2.getFirstInstant()+distanceIndices[1] 1655 return smallPets[petIdx], obj1.getFirstInstant()+distanceIndices[0], obj2.getFirstInstant()+distanceIndices[1]
1637 else: 1656 else:
1638 return None, None, None 1657 return None, None, None
1639 1658
1640 def predictPosition(self, instant, nTimeSteps, externalAcceleration = Point(0,0)): 1659 def predictPosition(self, instant, nTimeSteps, externalAcceleration = Point(0,0)):
1641 '''Predicts the position of object at instant+deltaT, 1660 '''Predicts the position of object at instant+deltaT,
1642 at constant speed''' 1661 at constant speed'''
1643 return predictPositionNoLimit(nTimeSteps, self.getPositionAtInstant(instant), self.getVelocityAtInstant(instant), externalAcceleration) 1662 return predictPositionNoLimit(nTimeSteps, self.getPositionAtInstant(instant), self.getVelocityAtInstant(instant), externalAcceleration)
1644 1663
1645 def projectCurvilinear(self, alignments, halfWidth = 3): 1664 def projectCurvilinear(self, alignments, halfWidth = 3):
1646 self.curvilinearPositions = CurvilinearTrajectory.fromTrajectoryProjection(self.getPositions(), alignments, halfWidth) 1665 self.curvilinearPositions = CurvilinearTrajectory.fromTrajectoryProjection(self.getPositions(), alignments, halfWidth)
1647 1666
1648 def computeSmoothTrajectory(self, minCommonIntervalLength): 1667 def computeSmoothTrajectory(self, minCommonIntervalLength):
1649 '''Computes the trajectory as the mean of all features 1668 '''Computes the trajectory as the mean of all features
1650 if a feature exists, its position is 1669 if a feature exists, its position is
1651 1670
1652 Warning work in progress 1671 Warning work in progress
1653 TODO? not use the first/last 1-.. positions''' 1672 TODO? not use the first/last 1-.. positions'''
1654 nFeatures = len(self.features) 1673 nFeatures = len(self.features)
1655 if nFeatures == 0: 1674 if nFeatures == 0:
1656 print('Empty object features\nCannot compute smooth trajectory') 1675 print('Empty object features\nCannot compute smooth trajectory')
1724 f.positions = Trajectory(pp) 1743 f.positions = Trajectory(pp)
1725 self.userTypes = {} 1744 self.userTypes = {}
1726 1745
1727 def classifyUserTypeHoGSVMAtInstant(self, img, instant, width, height, px, py, minNPixels, rescaleSize, orientations, pixelsPerCell, cellsPerBlock, blockNorm): 1746 def classifyUserTypeHoGSVMAtInstant(self, img, instant, width, height, px, py, minNPixels, rescaleSize, orientations, pixelsPerCell, cellsPerBlock, blockNorm):
1728 '''Extracts the image box around the object 1747 '''Extracts the image box around the object
1729 (of square size max(width, height) of the box around the features, 1748 (of square size max(width, height) of the box around the features,
1730 with an added px or py for width and height (around the box)) 1749 with an added px or py for width and height (around the box))
1731 computes HOG on this cropped image (with parameters rescaleSize, orientations, pixelsPerCell, cellsPerBlock) 1750 computes HOG on this cropped image (with parameters rescaleSize, orientations, pixelsPerCell, cellsPerBlock)
1732 and applies the SVM model on it''' 1751 and applies the SVM model on it'''
1733 croppedImg = cvutils.imageBox(img, self, instant, width, height, px, py, minNPixels) 1752 croppedImg = cvutils.imageBox(img, self, instant, width, height, px, py, minNPixels)
1734 if croppedImg is not None and len(croppedImg) > 0: 1753 if croppedImg is not None and len(croppedImg) > 0:
1741 '''Agregates SVM detections in each image and returns probability 1760 '''Agregates SVM detections in each image and returns probability
1742 (proportion of instants with classification in each category) 1761 (proportion of instants with classification in each category)
1743 1762
1744 images is a dictionary of images indexed by instant 1763 images is a dictionary of images indexed by instant
1745 With default parameters, the general (ped-bike-car) classifier will be used 1764 With default parameters, the general (ped-bike-car) classifier will be used
1746 1765
1747 Considered categories are the keys of speedProbabilities''' 1766 Considered categories are the keys of speedProbabilities'''
1748 if not hasattr(self, 'aggregatedSpeed') or not hasattr(self, 'userTypes'): 1767 if not hasattr(self, 'aggregatedSpeed') or not hasattr(self, 'userTypes'):
1749 print('Initializing the data structures for classification by HoG-SVM') 1768 print('Initializing the data structures for classification by HoG-SVM')
1750 self.initClassifyUserTypeHoGSVM(aggregationFunc, pedBikeCarSVM, bikeCarSVM, pedBikeSpeedTreshold, bikeCarSpeedThreshold, nInstantsIgnoredAtEnds) 1769 self.initClassifyUserTypeHoGSVM(aggregationFunc, pedBikeCarSVM, bikeCarSVM, pedBikeSpeedTreshold, bikeCarSpeedThreshold, nInstantsIgnoredAtEnds)
1751 1770
1773 else: 1792 else:
1774 self.setUserType(utils.argmaxDict(userTypeProbabilities)) 1793 self.setUserType(utils.argmaxDict(userTypeProbabilities))
1775 1794
1776 def classifyUserTypeArea(self, areas, homography): 1795 def classifyUserTypeArea(self, areas, homography):
1777 '''Classifies the object based on its location (projected to image space) 1796 '''Classifies the object based on its location (projected to image space)
1778 areas is a dictionary of matrix of the size of the image space 1797 areas is a dictionary of matrix of the size of the image space
1779 for different road users possible locations, indexed by road user type names 1798 for different road users possible locations, indexed by road user type names
1780 1799
1781 TODO: areas could be a wrapper object with a contains method that would work for polygons and images (with wrapper class) 1800 TODO: areas could be a wrapper object with a contains method that would work for polygons and images (with wrapper class)
1782 skip frames at beginning/end?''' 1801 skip frames at beginning/end?'''
1783 print('not implemented/tested yet') 1802 print('not implemented/tested yet')
1846 used for series of ground truth annotations using bounding boxes 1865 used for series of ground truth annotations using bounding boxes
1847 and for the output of Urban Tracker http://www.jpjodoin.com/urbantracker/ 1866 and for the output of Urban Tracker http://www.jpjodoin.com/urbantracker/
1848 1867
1849 By default in image space 1868 By default in image space
1850 1869
1851 Its center is the center of the box (generalize to other shapes?) 1870 Its center is the center of the box (generalize to other shapes?)
1852 (computed after projecting if homography available) 1871 (computed after projecting if homography available)
1853 ''' 1872 '''
1854 1873
1855 def __init__(self, num = None, timeInterval = None, topLeftPositions = None, bottomRightPositions = None, userType = userType2Num['unknown']): 1874 def __init__(self, num = None, timeInterval = None, topLeftPositions = None, bottomRightPositions = None, userType = userType2Num['unknown']):
1856 super(BBMovingObject, self).__init__(num, timeInterval, userType = userType) 1875 super(BBMovingObject, self).__init__(num, timeInterval, userType = userType)
1904 gt number of GT.frames 1923 gt number of GT.frames
1905 1924
1906 if returnMatches is True, return as 2 new arguments the GT and TO matches 1925 if returnMatches is True, return as 2 new arguments the GT and TO matches
1907 matches is a dict 1926 matches is a dict
1908 matches[i] is the list of matches for GT/TO i 1927 matches[i] is the list of matches for GT/TO i
1909 the list of matches is a dict, indexed by time, for the TO/GT id matched at time t 1928 the list of matches is a dict, indexed by time, for the TO/GT id matched at time t
1910 (an instant t not present in matches[i] at which GT/TO exists means a missed detection or false alarm) 1929 (an instant t not present in matches[i] at which GT/TO exists means a missed detection or false alarm)
1911 1930
1912 TODO: Should we use the distance as weights or just 1/0 if distance below matchingDistance? 1931 TODO: Should we use the distance as weights or just 1/0 if distance below matchingDistance?
1913 (add argument useDistanceForWeights = False)''' 1932 (add argument useDistanceForWeights = False)'''
1914 from munkres import Munkres 1933 from munkres import Munkres
1915 1934
1916 munk = Munkres() 1935 munk = Munkres()
1917 dist = 0. # total distance between GT and TO 1936 dist = 0. # total distance between GT and TO
1918 ct = 0 # number of associations between GT and tracker output in each frame 1937 ct = 0 # number of associations between GT and tracker output in each frame
1919 gt = 0 # number of GT.frames 1938 gt = 0 # number of GT.frames
1920 mt = 0 # number of missed GT.frames (sum of the number of GT not detected in each frame) 1939 mt = 0 # number of missed GT.frames (sum of the number of GT not detected in each frame)
1982 elif matches[a] in list(previousMatches.values()): 2001 elif matches[a] in list(previousMatches.values()):
1983 mismatches.append(matches[a]) 2002 mismatches.append(matches[a])
1984 for a in previousMatches: 2003 for a in previousMatches:
1985 if a not in matches and previousMatches[a] in list(matches.values()): 2004 if a not in matches and previousMatches[a] in list(matches.values()):
1986 mismatches.append(previousMatches[a]) 2005 mismatches.append(previousMatches[a])
1987 if debug: 2006 if debug:
1988 for mm in set(mismatches): 2007 for mm in set(mismatches):
1989 print('{} {}'.format(type(mm), mm.getNum())) 2008 print('{} {}'.format(type(mm), mm.getNum()))
1990 # some object mismatches may appear twice 2009 # some object mismatches may appear twice
1991 mme += len(set(mismatches)) 2010 mme += len(set(mismatches))
1992 2011
2015 suite = doctest.DocFileSuite('tests/moving.txt') 2034 suite = doctest.DocFileSuite('tests/moving.txt')
2016 #suite = doctest.DocTestSuite() 2035 #suite = doctest.DocTestSuite()
2017 unittest.TextTestRunner().run(suite) 2036 unittest.TextTestRunner().run(suite)
2018 #doctest.testmod() 2037 #doctest.testmod()
2019 #doctest.testfile("example.txt") 2038 #doctest.testfile("example.txt")
2020 if shapelyAvailable: 2039 if shapelyAvailable:
2021 suite = doctest.DocFileSuite('tests/moving_shapely.txt') 2040 suite = doctest.DocFileSuite('tests/moving_shapely.txt')
2022 unittest.TextTestRunner().run(suite) 2041 unittest.TextTestRunner().run(suite)