Mercurial Hosting > traffic-intelligence
changeset 1096:9a32d63bae3f
update from Lionel Nebot Janvier
author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
---|---|
date | Fri, 15 Feb 2019 14:35:56 -0500 |
parents | e53c6e87bb3f |
children | b3f8b26ee838 |
files | trafficintelligence/moving.py |
diffstat | 1 files changed, 74 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/trafficintelligence/moving.py Wed Feb 06 12:55:10 2019 -0500 +++ b/trafficintelligence/moving.py Fri Feb 15 14:35:56 2019 -0500 @@ -153,10 +153,10 @@ # We will use the polygon class of Shapely class STObject(object): - '''Class for spatio-temporal object, i.e. with temporal and spatial existence + '''Class for spatio-temporal object, i.e. with temporal and spatial existence (time interval and bounding polygon for positions (e.g. rectangle)). - It may not mean that the object is defined + It may not mean that the object is defined for all time instants within the time interval''' def __init__(self, num = None, timeInterval = None, boundingPolygon = None): @@ -446,9 +446,9 @@ return Point(X,Y) def getSYfromXY(p, alignments, goodEnoughAlignmentDistance = 0.5): - ''' Snap a point p to its nearest subsegment of it's nearest alignment (from the list alignments). - A alignment is a list of points (class Point), most likely a trajectory. - + ''' Snap a point p to its nearest subsegment of it's nearest alignment (from the list alignments). + A alignment is a list of points (class Point), most likely a trajectory. + Output: ======= [alignment index, @@ -471,7 +471,7 @@ print('Error: Alignment {0}, segment {1} has identical bounds and therefore is not a vector. Projection cannot continue.'.format(alignmentIdx, alignment_p)) return None # check if the projected point is in between the current segment of the alignment bounds - 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): + 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): offsetY = Point.distanceNorm2(closestPoint, p) if offsetY < minOffsetY: minOffsetY = offsetY @@ -479,7 +479,7 @@ snappedAlignmentLeadingPoint = alignment_p snappedPoint = Point(closestPoint.x, closestPoint.y) #Jump loop if significantly close - if offsetY < goodEnoughAlignmentDistance: + if offsetY < goodEnoughAlignmentDistance: break #Get sub-segment distance @@ -497,10 +497,10 @@ return None def getXYfromSY(s, y, alignmentNum, alignments): - ''' Find X,Y coordinate from S,Y data. + ''' Find X,Y coordinate from S,Y data. if mode = 0 : return Snapped X,Y if mode !=0 : return Real X,Y - ''' + ''' alignment = alignments[alignmentNum] i = 1 while s > alignment.getCumulativeDistance(i) and i < len(alignment): @@ -595,7 +595,7 @@ return p1+dp12.__mul__(ua) # def intersection(p1, p2, dp1, dp2): -# '''Returns the intersection point between the two lines +# '''Returns the intersection point between the two lines # defined by the respective vectors (dp) and origin points (p)''' # from numpy import matrix # from numpy.linalg import linalg @@ -617,7 +617,7 @@ return None else: inter = intersection(p1, p2, p3, p4) - if (inter is not None + if (inter is not None and utils.inBetween(p1.x, p2.x, inter.x) and utils.inBetween(p3.x, p4.x, inter.x) and utils.inBetween(p1.y, p2.y, inter.y) @@ -863,7 +863,7 @@ print('Index {} beyond trajectory length {}'.format(i, self.length())) def getMaxDistance(self, metric): - 'Returns the maximum distance between points in the trajectory' + 'Returns the maximum distance between points in the trajectory' positions = self.getPositions().asArray().T return cdist(positions, positions, metric = metric).max() @@ -885,7 +885,7 @@ return i def similarOrientation(self, refDirection, cosineThreshold, minProportion = 0.5): - '''Indicates whether the minProportion (<=1.) (eg half) of the trajectory elements (vectors for velocity) + '''Indicates whether the minProportion (<=1.) (eg half) of the trajectory elements (vectors for velocity) have a cosine with refDirection is smaller than cosineThreshold''' count = 0 lengthThreshold = float(self.length())*minProportion @@ -902,8 +902,8 @@ return None def getIntersections(self, p1, p2): - '''Returns a list of the indices at which the trajectory - intersects with the segment of extremities p1 and p2 + '''Returns a list of the indices at which the trajectory + intersects with the segment of extremities p1 and p2 Returns an empty list if there is no crossing''' indices = [] intersections = [] @@ -924,8 +924,8 @@ return indices, intersections def getLineIntersections(self, p1, p2): - '''Returns a list of the indices at which the trajectory - intersects with the line going through p1 and p2 + '''Returns a list of the indices at which the trajectory + intersects with the line going through p1 and p2 Returns an empty list if there is no crossing''' indices = [] intersections = [] @@ -972,7 +972,7 @@ '''Returns the trajectory built with the set of points inside the (shapely) polygon The polygon could be a prepared polygon (faster) from prepared.prep - t2 is another trajectory (could be velocities) + t2 is another trajectory (could be velocities) which is filtered based on the first (self) trajectory''' traj = Trajectory() inPolygon = [] @@ -1021,8 +1021,8 @@ '''Sub class of trajectory for trajectories with curvilinear coordinates and lane assignements longitudinal coordinate is stored as first coordinate (exterior name S) lateral coordinate is stored as second coordinate - the third "lane" coordinate is for an alignment id, - whether explicit for a list/dict of alignments, + the third "lane" coordinate is for an alignment id, + whether explicit for a list/dict of alignments, or implicit for a road with lane numbers''' def __init__(self, S = None, Y = None, lanes = None): @@ -1084,7 +1084,7 @@ [align, alignPoint, snappedPoint, subsegmentDistance, S, Y] = result curvilinearPositions.addPositionSYL(S, Y, align) - ## Go back through points and correct lane + ## Go back through points and correct lane #Run through objects looking for outlier point smoothed_lanes = utils.filterCategoricalMovingWindow(curvilinearPositions.getLanes(), halfWidth) ## Recalculate projected point to new lane @@ -1103,7 +1103,7 @@ curvilinearPositions.setPosition(i, S, Y, align) return curvilinearPositions - def __getitem__(self,i): + def __getitem__(self,i): if isinstance(i, int): return [self.positions[0][i], self.positions[1][i], self.lanes[i]] else: @@ -1141,7 +1141,7 @@ return diff def getIntersections(self, S1, lane = None): - '''Returns a list of the indices at which the trajectory + '''Returns a list of the indices at which the trajectory goes past the curvilinear coordinate S1 (in provided lane if lane is not None) Returns an empty list if there is no crossing''' @@ -1173,8 +1173,8 @@ carClassifier = CarClassifier() class MovingObject(STObject, VideoFilenameAddable): - '''Class for moving objects: a spatio-temporal object - with a trajectory and a geometry (constant volume over time) + '''Class for moving objects: a spatio-temporal object + with a trajectory and a geometry (constant volume over time) and a usertype (e.g. road user) coded as a number (see userTypeNames) ''' @@ -1241,25 +1241,44 @@ inter, self.positions, self.velocities = MovingObject.aggregateTrajectories(self.features, self.getTimeInterval()) def updateCurvilinearPositions(self, method, changeOfAlignment, nextAlignment_idx, timeStep = None, time = None, - leaderVehicleCurvilinearPositionAtPrecedentTime = None, previousAlignmentId = None, - maxSpeed = None, acceleration = None, seed = None, delta = None): - import math + leaderVehicle = None, previousAlignmentId = None, + maxSpeed = None, acceleration = None): if method == 'newell': - self.curvilinearPositions.addPositionSYL(leaderVehicleCurvilinearPositionAtPrecedentTime - self.dn, 0, nextAlignment_idx) - if changeOfAlignment: - self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep, - (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-1][1])/timeStep, - (previousAlignmentId, nextAlignment_idx)) + if time <= self.timeInterval[0] < time + timeStep: + # #si t < instant de creation du vehicule, la position vaut l'espacement dn entre les deux vehicules + if leaderVehicle is None: + self.curvilinearPositions.addPositionSYL( + -self.dn, + 0, + nextAlignment_idx) + else: + self.curvilinearPositions.addPositionSYL( + leaderVehicle.curvilinearPositions[0][0] - self.dn, + 0, + nextAlignment_idx) else: - self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep, - (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-1][1])/timeStep, - None) - elif method == 'constantSpeed': - random.seed(seed) - self.curvilinearPositions.addPositionSYL(self.curvilinearPositions[time-1][0] + self.desiredSpeed * timeStep, - 0, - nextAlignment_idx) + if leaderVehicle is None: + self.curvilinearPositions.addPositionSYL(self.curvilinearPositions[-1][0]+self.desiredSpeed*timeStep, 0, nextAlignment_idx) + else: + if time > self.reactionTime: + previousVehicleCurvilinearPositionAtPrecedentTime = \ + leaderVehicle.curvilinearPositions[-int(round(self.reactionTime))][0] # t-v.reactionTime + else: + previousVehicleCurvilinearPositionAtPrecedentTime = \ + leaderVehicle.curvilinearPositions[0][0] + + self.curvilinearPositions.addPositionSYL(previousVehicleCurvilinearPositionAtPrecedentTime - self.dn, 0, nextAlignment_idx) + + #mise ajour des vitesses + if changeOfAlignment: + self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep, + (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-2][1])/timeStep, + (previousAlignmentId, nextAlignment_idx)) + else: + self.velocities.addPositionSYL((self.curvilinearPositions[-1][0]-self.curvilinearPositions[-2][0])/timeStep, + (self.curvilinearPositions[-1][1]-self.curvilinearPositions[-2][1])/timeStep, + None) @staticmethod def concatenate(obj1, obj2, num = None, newFeatureNum = None, computePositions = False): '''Concatenates two objects, whether overlapping temporally or not @@ -1449,8 +1468,8 @@ return [] else: tmp = utils.sortByLength(self.getFeatures(), reverse = True) - return tmp[:min(len(tmp), nFeatures)] - + return tmp[:min(len(tmp), nFeatures)] + def getFeatureNumbersOverTime(self): '''Returns the number of features at each instant dict instant -> number of features''' @@ -1558,7 +1577,7 @@ @staticmethod def distances(obj1, obj2, instant1, _instant2 = None): - '''Returns the distances between all features of the 2 objects + '''Returns the distances between all features of the 2 objects at the same instant instant1 or at instant1 and instant2''' if _instant2 is None: @@ -1638,7 +1657,7 @@ return None, None, None def predictPosition(self, instant, nTimeSteps, externalAcceleration = Point(0,0)): - '''Predicts the position of object at instant+deltaT, + '''Predicts the position of object at instant+deltaT, at constant speed''' return predictPositionNoLimit(nTimeSteps, self.getPositionAtInstant(instant), self.getVelocityAtInstant(instant), externalAcceleration) @@ -1647,8 +1666,8 @@ def computeSmoothTrajectory(self, minCommonIntervalLength): '''Computes the trajectory as the mean of all features - if a feature exists, its position is - + if a feature exists, its position is + Warning work in progress TODO? not use the first/last 1-.. positions''' nFeatures = len(self.features) @@ -1726,7 +1745,7 @@ def classifyUserTypeHoGSVMAtInstant(self, img, instant, width, height, px, py, minNPixels, rescaleSize, orientations, pixelsPerCell, cellsPerBlock, blockNorm): '''Extracts the image box around the object - (of square size max(width, height) of the box around the features, + (of square size max(width, height) of the box around the features, with an added px or py for width and height (around the box)) computes HOG on this cropped image (with parameters rescaleSize, orientations, pixelsPerCell, cellsPerBlock) and applies the SVM model on it''' @@ -1743,7 +1762,7 @@ images is a dictionary of images indexed by instant With default parameters, the general (ped-bike-car) classifier will be used - + Considered categories are the keys of speedProbabilities''' if not hasattr(self, 'aggregatedSpeed') or not hasattr(self, 'userTypes'): print('Initializing the data structures for classification by HoG-SVM') @@ -1775,7 +1794,7 @@ def classifyUserTypeArea(self, areas, homography): '''Classifies the object based on its location (projected to image space) - areas is a dictionary of matrix of the size of the image space + areas is a dictionary of matrix of the size of the image space for different road users possible locations, indexed by road user type names TODO: areas could be a wrapper object with a contains method that would work for polygons and images (with wrapper class) @@ -1848,7 +1867,7 @@ By default in image space - Its center is the center of the box (generalize to other shapes?) + Its center is the center of the box (generalize to other shapes?) (computed after projecting if homography available) ''' @@ -1906,13 +1925,13 @@ if returnMatches is True, return as 2 new arguments the GT and TO matches matches is a dict matches[i] is the list of matches for GT/TO i - the list of matches is a dict, indexed by time, for the TO/GT id matched at time t + the list of matches is a dict, indexed by time, for the TO/GT id matched at time t (an instant t not present in matches[i] at which GT/TO exists means a missed detection or false alarm) TODO: Should we use the distance as weights or just 1/0 if distance below matchingDistance? (add argument useDistanceForWeights = False)''' from munkres import Munkres - + munk = Munkres() dist = 0. # total distance between GT and TO ct = 0 # number of associations between GT and tracker output in each frame @@ -1984,7 +2003,7 @@ for a in previousMatches: if a not in matches and previousMatches[a] in list(matches.values()): mismatches.append(previousMatches[a]) - if debug: + if debug: for mm in set(mismatches): print('{} {}'.format(type(mm), mm.getNum())) # some object mismatches may appear twice @@ -2017,6 +2036,6 @@ unittest.TextTestRunner().run(suite) #doctest.testmod() #doctest.testfile("example.txt") - if shapelyAvailable: + if shapelyAvailable: suite = doctest.DocFileSuite('tests/moving_shapely.txt') unittest.TextTestRunner().run(suite)