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