Mercurial Hosting > 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) |