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)