tvaLib
prediction_DMP.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # tvaLib Copyright (c) 2012-2016 Paul G. St-Aubin
3 # Ecole Polytechnique de Montreal, McGill University
4 # Python 2.7; (dt) Spyder Windows 10 64-bit; ipython Ubuntu 15.04 64-bit
5 
9 import os, sys
10 import cPickle as pickle
11 from inspect import getfile as inspect_getfile
12 from inspect import currentframe as inspect_currentframe
13 
14 if __name__ == '__main__':
15  print('IntPrediction library loaded directly.')
16  sys.path.insert(0,os.path.dirname(os.path.dirname(os.path.abspath(inspect_getfile(inspect_currentframe())))))
17 import lib.tools as tvaLib
18 import lib.int as tvaInteraction
19 import include.config as tvaConfig; oldstdout = sys.stdout;sys.stdout = tvaConfig.NullWriter() #Disable output
20 
21 try:
22  #Scipy
23  try:
24  from scipy.sparse import lil_matrix
25  from scipy.sparse import csr_matrix
26  from scipy.sparse import find as scipy_sparse_find
27  except ImportError: raise Exception, [0103, 'SciPy is not installed.']
28  #Numpy
29  try: import numpy as np
30  except ImportError: raise Exception, [0101, 'Numpy is not installed.']
31 finally:
32  sys.stdout = oldstdout #Re-enable output
33 
34 
37 class Dimension():
38  def __init__(self, min, max, step=0, steps=0, observations=None):
39  if(min < max):
40  self.min = float(min)
41  self.max = float(max)
42  else:
43  self.min = float(max)
44  self.max = float(min)
45  if(step):
46  steps = 0
47  self.step = float(step)
48  self.steps = steps
49 
50  self.range = []
51  self.is_percentile = False
52 
53  self.genRange(observations=observations)
54 
55 
56  def __len__(self): return len(self.range)
57  def __getitem__(self,i): return self.range[i]
58 
60  return_val = self.getRange()
61  if(len(return_val) >= 2): return self.getRange()[:-1]
62  else: return []
64  return_val = self.getRange()
65  if(len(return_val) >= 2): return self.getRange()[1:]
66  else: return []
67 
68  def getRange(self):
69  if(not self.range): self.genRange()
70  return self.range
71 
72 
73  def getMidRangeValue(self, index):
74  if(index + 1 >= len(self.range)): return self.range[-1]
75  return (self.range[index] + self.range[index+1])/2
76 
77  def genRange(self, observations=None):
78  ''' If *observations* is defined along with the number of steps, this function returns a percintile based range. '''
79  if(observations and self.steps):
80  self.is_percentile = True
81  self.range = tvaLib.Math.getPercintileBinsFromList(observations, bins=self.steps, includeLeftEdge=True)
82  else:
83  self.is_percentile = False
84 
85  self.range = [self.min]
86  if(self.step):
87  while(True):
88  next_val = self.range[-1]+self.step
89  if(next_val >= self.max):
90  self.range.append(self.max)
91  break
92  else:
93  self.range.append(next_val)
94 
95  elif(self.steps):
96  if(self.steps > 1):
97  step = float(self.max-self.min)/(self.steps-1)
98  for i in range(self.steps-1):
99  self.range.append(self.range[-1]+step)
100  else:
101  self.range = []
102  return True
103 
104  def setRange(self, value):
105  self.range = value
106  return True
107 
108  def findIDofLowerRangeFromValue(self, value):
109  if(not self.range): self.getRange()
110  return_val = min(self.range, key=lambda x:abs(x-value))
111  return_key = [i for i,x in enumerate(self.range) if x == return_val][0]
112  if(return_key == 0): return 0
113  if(value-return_val >= 0): return return_key
114  else: return return_key - 1
115 
116 class DimData():
117  def __init__(self, dtype='sparse_csr'):
118  ''' Contain spatial data efficiently. As a memory-saving feature, the
119  sparse matrix is only generated as needed using self.getData(). '''
120  self.type = dtype
121 
122  def __getitem__(self,i): return self.getData()[i[0],i[1]]
123  def __len__(self):
124  if(self.type in ['sparse_lil','sparse_csr']): return self.getData().nnz
125  else: return sum(sum(1 for i in row if i) for row in self.getData())
126 
127  def getMat(self):
128  if(self.type in ['sparse_lil','sparse_csr']): return np.array(self.getData().todense())
129  else: return self.getData()
130 
131  def incData(self, x, y, value=1):
132  self.getData()[x,y] += value
133  return True
134 
135  def setData(self, x, y, value):
136  self.getData()[x,y] = value
137  return True
138 
139  def getData(self):
140  ''' Pseudo-sparse sparse matrix. Requires global DimData_bins. '''
141  try: return self.data
142  except:
143  if(self.type == 'sparse_lil'): self.data = lil_matrix((DimData_bins[0],DimData_bins[1]))
144  elif(self.type == 'sparse_csr'): self.data = csr_matrix((DimData_bins[0],DimData_bins[1]))
145  else: self.data = np.zeros((DimData_bins[0],DimData_bins[1]))
146  return self.data
147 
148  def populate(self, mat):
149  for i in range(len(mat)):
150  for j in range(len(mat[i])):
151  self.setData(self, i, j, mat[i][j])
152  return True
153 
154  def convertToType(self, dtype):
155  try: # Data might not yet be set
156  if(self.type == 'sparse_lil'):
157  if(dtype == 'sparse_csr'): self.data = self.data.tocsr()
158  else: self.data = self.data.todense()
159  elif(self.type == 'sparse_csr'):
160  if(dtype == 'sparse_lil'): self.data = self.data.tolil()
161  else: self.data = self.data.todense()
162  else:
163  if(dtype == 'sparse_lil'): self.data = lil_matrix(self.data)
164  elif(dtype == 'sparse_csr'): self.data = csr_matrix(self.data)
165  except AttributeError: pass
166  self.type = dtype
167 
168  def nonzero(self):
169  try: return self.data.nonzero()
170  except AttributeError: return []
171 
172  def eliminate_zeros(self):
173  try: self.data.eliminate_zeros()
174  except AttributeError: pass
175 
176  def sum(self):
177  return self.getData().sum()
178 
179  def multiply(self, target):
180  ''' Multiply the cells of this DimData with a DimData of similar type '''
181  if(self.type == 'sparse_lil' or self.type == 'sparse_csr'): return self.getData().multiply(target.getData())
182  else: return np.multiply(self.getData(), target.getData())
183 
185  def __init__(self, alignments, xy_bounds, userTypes=7, maxSpeed=80, speed_conv=54, objects=None, timeHorizon=None, speedBinNum=7, curvilinearBinSize=1, motPatBinNum=100, probRetentionThreshold=0.00001, minSampleSize=50, motPatBinSize=[1,1], verbose=0, **kwargs):
186  ''' Dimensions:
187  ===========
188  Input:
189  -dt [0, timeHorizon] step = 1
190  -speed [0, maxSpeed[ steps = speedBinNum
191  -lane [0, len(alignments)] step = 1
192  -curvlinear distance [.getSCoordinates()[0], .getSCoordinates()[-1][ step = curvilinearBinSize (in metres)
193  -userType [0, userTypes] step = 1
194  Output:
195  -xrange steps = motPatBinNum
196  -yrange steps = motPatBinNum
197 
198  Parameters:
199  ===========
200  timehorizon: determines the temporal dimension bound of the motion
201  pattern with a step size of 1 [frame].
202  timehorizon: determines the user type bound of the motion pattern
203  with a step size of 1 [user type].
204 
205  Sample usage:
206  =============
207  motpat = MotionPattern(alignments=alignments, timeHorizon=100, maxSpeed=80, objects=objects)
208  motpat.prepare() #or use motpat.learnMotionPattern(objects)
209 
210  Data Structure:
211  ===============
212  data[lane][curvy][speed][userType][deltatime].getMat()[x][y]
213  '''
214 
215 
216 
217 
218  if(not timeHorizon and objects):
219  timeHorizon = 0
220  for i in objects:
221  if(timeHorizon < int(i.length())): timeHorizon = int(i.length())
222  if(not timeHorizon): timeHorizon = 120
223 
224 
225  self.verbose = verbose
226  self.speed_conv = speed_conv
227  self.data = []
228  self.sparse_type = 'sparse_csr'
229  self.alignSkipList = [[0,2],[0,3],[1,2],[1,3],[6,7],[6,5],[4,7],[4,5]]
230  self.motPatBinNum = motPatBinNum
231  # Generate dimensions
232  self.lane = Dimension(0, len(alignments)-1, step=1)
233  self.userType = Dimension(0, userTypes, step=1)
234  self.curvy = []
235  for lane in range(len(self.lane)):
236  self.curvy.append(Dimension(alignments[lane].getSCoordinates()[0], alignments[lane].getSCoordinates()[-1], step=curvilinearBinSize))
237  self.speed = Dimension(0, maxSpeed, steps=speedBinNum)
238  self.deltatime = Dimension(0, timeHorizon, step=1)
239  self.xrange = Dimension(xy_bounds[0].lower, xy_bounds[0].upper, steps=motPatBinNum)
240  self.yrange = Dimension(xy_bounds[1].lower, xy_bounds[1].upper, steps=motPatBinNum)
241  self.wasImported = False
242  self.name = 'discretised motion pattern'
243  self.minSampleSize = minSampleSize
244  self.probRetentionThreshold = probRetentionThreshold
245 
246  global DimData_bins #global variable for DimData as a space-saving feature
247  DimData_bins = [self.motPatBinNum,self.motPatBinNum]
248 
249  def __iter__(self): return iter(tvaLib.flatten_list(self.data))
250  def getSize(self): return int(len(self.lane)*len(self.speed)*len(self.deltatime)*len(self.xrange)*len(self.yrange)*len(self.userType)*sum([len(curv_) for curv_ in self.curvy])/float(len(self.curvy)))
251 
252 
253  def initiateMPASpace(self):
254 
255  for lane in range(len(self.lane)):
256  self.data.append([])
257  for curvy in range(len(self.curvy[lane].getRangeLowerIntervalList())):
258  self.data[-1].append([])
259  for speed in range(len(self.speed.getRangeLowerIntervalList())):
260  self.data[-1][-1].append([])
261  for userType in range(len(self.userType)):
262  self.data[-1][-1][-1].append([])
263  for deltatime in range(len(self.deltatime.getRangeLowerIntervalList())):
264  self.data[-1][-1][-1][-1].append(DimData('sparse_csr'))
265 
266 
267  def prepare(self, objects=False, allowImport=True, importLocations='', customSpeedRange=None, version='', verbose=0, **kwargs):
268  ''' Input:
269  ======
270  objects: list of objects to use to build DMP
271  allowImport: allow DMP importing if set to true
272  importLocations: import locations
273  customSpeedRange: set custom speed dimension range
274 
275  '''
276  import_success = False
277  if(allowImport and importLocations): import_success = self.importDMP(paths=importLocations, version=version, verbose=verbose)
278 
279  if((not allowImport or not import_success) and objects):
280  self.learnMotionPattern(objects=objects, customSpeedRange=customSpeedRange, verbose=verbose)
281  self.pruneMotionPattern(verbose=verbose)
282  return True
283 
284  def export(self, path=None, version=None, **kwargs):
285  ''' Protocol-compatible export method. '''
286  if(not path or not version): return False
287  return self.exportDMP(path=path, version=version, **kwargs)
288 
289 
290  def importDMP(self, paths, version=None, filename='dmp.mp', verificationDepth=3, verbose=0):
291  ''' Attempt to import dmp.mp from any path above. '''
292  s_version = None
293  if(type(paths) != list): paths = [paths]
294  for path in paths:
295  if(os.path.exists(path) and os.path.isfile(os.path.join(path, filename))):
296  with open(os.path.join(path, filename), 'rb') as input_data:
297  s_version = pickle.load(input_data)
298  if(tvaLib.Parse.versionIsMoreRecent(version, s_version, verificationDepth, orEqual=False)): continue
299  tmp_dict = pickle.load(input_data)
300  self.__dict__.update(tmp_dict)
301  self.wasImported = True
302 
304  return True
305  if(s_version and verbose >= 1): tvaLib.printWarning('The motion pattern appears to be saved with an older version ('+s_version+'). The motion pattern will be automatically flushed. It is highly recommended to preload as many sequences as possible before this step (i.e. not use -f). It may be advisable to run the analysis again after saving the motion pattern to cache with -a.', indent=4)
306  return False
307 
308  def exportDMP(self, path, version=None, filename='dmp.mp', **kwargs):
309  ''' Export the contents of this instance. '''
310  if(self.wasImported): return False
311  with open(os.path.join(path, filename), 'wb') as output:
312  pickle.dump(version, output, protocol=2)
313  pickle.dump(self.__dict__, output, protocol=2)
314  return True
315 
316  def computeCrossingsCollisions(self, obj1, obj2, interval, triage=False, scatterReturnPosition=False, VolumetricCollisionDistanceThreshold=[2,4], timeHorizon=100, verbose=0):
317  ''' For this pair of road users (obj1 and obj2), determine collision
318  probability distribution (each "CP" is now a distribution instead
319  of a single point)
320 
321  VolumetricCollisionDistanceThreshold is the size (in standard
322  units) of an object's rectangular footprint. The rectangle is
323  aligned to the velocity vector and defined in the order
324  [width,length]
325  probRetentionThreshold has a large impact on performance.
326  The lower the number, the greater the accuracy, but the larger
327  memory requirement.
328 
329  Input:
330  ======
331  scatterReturnPosition: Artifically scatter returned position inside
332  of bin [Not implemented]
333 
334  Output:
335  =======
336  {t1: [[p1x, p1y, TTC, prob], [p2x, p2y, TTC, prob], ...], t2: ...} '''
337 
338  from moving import Point as TrafIntMoving_Point
339 
340 
341  has_samples = False
342  exists = False
343  if(verbose >= 3):
344  mpa_s_1 = []
345  mpa_s_2 = []
346 
347 
348 
349  indicator_list = {}
350  if(verbose >=5): print('Object # '+str(obj1.num)+' with object # '+str(obj2.num))
351  #import lib.vis as tvaVis
352  #fig, ax = tvaVis.Traj3D([obj1, obj2], alignments=triage)
353  for ti in range(interval.first,interval.last+1):
354  obj1_offset = ti - obj1.getTimeInterval().first
355  obj2_offset = ti - obj2.getTimeInterval().first
356 
357  if(triage):
358  try:
359  distHorizon1 = timeHorizon*obj1.velocities.positions[2][obj1_offset]
360  distHorizon2 = timeHorizon*obj2.velocities.positions[2][obj2_offset]
361  except: continue
362  if(not triage.getRouteDistHorizonByPreCalc(obj1.curvilinearPositions.getLanes()[obj1_offset], obj1.curvilinearPositions.getXCoordinates()[obj1_offset], obj2.curvilinearPositions.getLanes()[obj2_offset], obj2.curvilinearPositions.getXCoordinates()[obj2_offset], distHorizon1, distHorizon2)):
363  #ax.scatter([obj1.getXCoordinates()[obj1_offset]], [obj1.getYCoordinates()[obj1_offset]], [ti], marker='o', linewidth=10)
364  #ax.scatter([obj2.getXCoordinates()[obj2_offset]], [obj2.getYCoordinates()[obj2_offset]], [ti], marker='o', linewidth=10)
365 
366  continue
367  #if(([obj1.curvilinearPositions.getLanes()[obj1_offset], obj2.curvilinearPositions.getLanes()[obj2_offset]] in self.alignSkipList) or ([obj2.curvilinearPositions.getLanes()[obj1_offset], obj1.curvilinearPositions.getLanes()[obj2_offset]] in self.alignSkipList)): continue
368 
369  dmp1_lane = self.lane.findIDofLowerRangeFromValue(obj1.curvilinearPositions.getLanes()[obj1_offset])
370  dmp1_curvy = self.curvy[dmp1_lane].findIDofLowerRangeFromValue(obj1.curvilinearPositions.getXCoordinates()[obj1_offset])
371  dmp1_speed = self.speed.findIDofLowerRangeFromValue(obj1.getSpeeds()[obj1_offset])
372  dmp1_userType = self.userType.findIDofLowerRangeFromValue(obj1.getUserType())
373  dmp2_lane = self.lane.findIDofLowerRangeFromValue(obj2.curvilinearPositions.getLanes()[obj2_offset])
374  dmp2_curvy = self.curvy[dmp2_lane].findIDofLowerRangeFromValue(obj2.curvilinearPositions.getXCoordinates()[obj2_offset])
375  dmp2_speed = self.speed.findIDofLowerRangeFromValue(obj2.getSpeeds()[obj2_offset])
376  dmp2_userType = self.userType.findIDofLowerRangeFromValue(obj2.getUserType())
377 
378  indicator_list[ti] = []
379  probabilitySpace = []
380 
381  exists = True
382  has_probability = False
383 
384  for dmp_dt in range(1, int(timeHorizon)):
385  try:
386  if(verbose >= 3):
387  mpa_s_1.append(self.getDataSampleSize(dmp1_lane, dmp1_curvy, dmp1_speed, dmp1_userType, dmp_dt))
388  mpa_s_2.append(self.getDataSampleSize(dmp2_lane, dmp2_curvy, dmp2_speed, dmp2_userType, dmp_dt))
389  if(self.getDataSampleSize(dmp1_lane, dmp1_curvy, dmp1_speed, dmp1_userType, dmp_dt) < self.minSampleSize or self.getDataSampleSize(dmp2_lane, dmp2_curvy, dmp2_speed, dmp2_userType, dmp_dt) < self.minSampleSize):
390  probabilitySpace.append(csr_matrix((self.motPatBinNum,self.motPatBinNum)))
391  continue
392  except:
393  if(verbose >= 5):
394  for objId, dmp_lane, dmp_curvy, dmp_speed, dmp_userType in zip(['1','2'], [dmp1_lane,dmp2_lane], [dmp1_curvy,dmp2_curvy], [dmp1_speed,dmp2_speed], [dmp1_userType,dmp2_userType]):
395  try:
396  self.data[dmp_lane]
397  try:
398  self.data[dmp_lane][dmp_curvy]
399  try:
400  self.data[dmp_lane][dmp_curvy][dmp_speed]
401  try:
402  self.data[dmp_lane][dmp_curvy][dmp_userType]
403  try: self.data[dmp_lane][dmp_curvy][dmp_userType][dmp_speed][dmp_dt]
404  except IndexError: tvaLib.printWarning('There was an indexing problem with the timehorizon index ('+str(dmp_dt)+') of object '+objId+' and and the MPA timehorizon dimensions ('+str(len(self.data[dmp_lane][dmp_curvy][dmp_userType][dmp_speed]))+')', indent=4)
405  except IndexError: tvaLib.printWarning('There was an indexing problem with the userType index ('+str(dmp_userType)+') of object '+objId+' and and the MPA userType dimensions ('+str(len(self.data[dmp1_lane][dmp_curvy][dmp_userType]))+')', indent=4)
406  except IndexError: tvaLib.printWarning('There was an indexing problem with the speed index ('+str(dmp_speed)+') of object '+objId+' and and the MPA speed dimensions ('+str(len(self.data[dmp1_lane][dmp_curvy]))+')', indent=4)
407  except IndexError: tvaLib.printWarning('There was an indexing problem with the curvilinear index ('+str(dmp_curvy)+') of object '+objId+' and and the MPA curvilinear dimensions ('+str(len(self.data[dmp_lane]))+')', indent=4)
408  except IndexError: tvaLib.printWarning('There was an indexing problem with the lane index ('+str(dmp_lane)+') of object '+objId+' and and the MPA lane dimensions ('+str(len(self.data))+')', indent=4)
409  continue
410 
411  has_samples = True
412  # Joint probability
413  joint_probability = (self.data[dmp1_lane][dmp1_curvy][dmp1_speed][dmp1_userType][dmp_dt].multiply(self.data[dmp2_lane][dmp2_curvy][dmp2_speed][dmp2_userType][dmp_dt]))/(self.data[dmp1_lane][dmp1_curvy][dmp1_speed][dmp1_userType][dmp_dt].sum()*self.data[dmp2_lane][dmp2_curvy][dmp2_speed][dmp2_userType][dmp_dt].sum())
414  probabilitySpace.append(joint_probability)
415  if(joint_probability.nnz): has_probability = True
416 
417  '''# Debug
418  if(verbose >= 4):
419  if(joint_probability.sum() > 0.001):
420  print(joint_probability.sum(), self.getDataSampleSize(dmp1_lane, dmp1_curvy, dmp1_speed, dmp1_userType, dmp_dt), self.getDataSampleSize(dmp2_lane, dmp2_curvy, dmp2_speed, dmp1_userType, dmp_dt), ti, dmp_dt, obj1.num, obj2.num)
421  if(verbose >= 5):
422  import matplotlib.pyplot as plt
423  fig = plt.figure('Joint probability', figsize=(12, 15))
424  ax = plt.subplot2grid((2,2),(0, 0), colspan=2)
425  plt.imshow(joint_probability.getMat())
426  cb = plt.colorbar()
427  ax = plt.subplot2grid((2,2),(1, 0))
428  plt.imshow(self.data[dmp1_lane][dmp1_curvy][dmp1_speed][dmp1_userType][dmp_dt].getMat())
429  cb = plt.colorbar()
430  ax = plt.subplot2grid((2,2),(1, 1), colspan=2)
431  plt.imshow(self.data[dmp2_lane][dmp2_curvy][dmp2_speed][dmp2_userType][dmp_dt].getMat())
432  cb = plt.colorbar()'''
433 
434 
435 
436 
437 
439  if(has_probability):
440  TTC = lil_matrix((self.motPatBinNum,self.motPatBinNum)) #Lil_matrix is more efficient for changing sparsity (adding cells)
441  prob = {}
442  for dt in range(len(probabilitySpace)):
443  i,j,vals = scipy_sparse_find(probabilitySpace[dt])
444  for m in range(len(vals)):
445  if(vals[m] > self.probRetentionThreshold):
446  TTC[i[m],j[m]] += vals[m]*dt
447  try: prob[str(i[m])+','+str(j[m])].append(vals[m])
448  except: prob[str(i[m])+','+str(j[m])] = [vals[m]]
449  if(TTC.nnz):
450  i,j,TTCs = scipy_sparse_find(TTC)
451  for m in range(len(TTCs)):
452  #prob[str(i[m])+','+str(j[m])] is a list of probabilities for events at point (i[m],j[m]) at different predictions dt from the instant ti
453  prob_ = tvaLib.Math.setTheoryUnionNIndEvents(prob[str(i[m])+','+str(j[m])], recursionDepthEstimation=10, smallValueLimit=0.05)
454  #[x,y,prob,TTC,sampleSize1,sampleSize2]
455  indicator_list[ti].append(tvaInteraction.SafetyPoint(p = TrafIntMoving_Point(self.xrange.getMidRangeValue(i[m]),
456  self.yrange.getMidRangeValue(j[m])),
457  probability = prob_,
458  indicator = TTCs[m]/sum(prob[str(i[m])+','+str(j[m])]),
459  sampleSize1 = self.getDataSampleSize(dmp1_lane, dmp1_curvy, dmp1_speed, dmp1_userType, dmp_dt),
460  sampleSize2 = self.getDataSampleSize(dmp2_lane, dmp2_curvy, dmp2_speed, dmp2_userType, dmp_dt)))
461  if(verbose >= 3 and not has_samples and exists): tvaLib.printWarning('User pair between obj #'+str(obj1.num)+' and obj #'+str(obj2.num)+' has too few MPA samples (avg1: '+str(round(sum(mpa_s_1)/float(len(mpa_s_1)),2))+', max1: '+str(max(mpa_s_1))+', avg2: '+str(round(sum(mpa_s_2)/float(len(mpa_s_2)),2))+', max2: '+str(max(mpa_s_2))+').', indent=4)
462  return indicator_list
463 
464  def learnMotionPattern(self, objects, customSpeedRange=None, verbose=0):
465  ''' The Motion Pattern is simply learned by observing and recording all
466  traffic data over the dimensions of the Motion Pattern. '''
467 
468 
469  if(customSpeedRange): self.speed.setRange(customSpeedRange)
470  self.initiateMPASpace()
471  skipped = {'lanes':0, 'align':0, 'speed':0, 'userType':0, 'time':0, 'x':0, 'y':0, 'unknown':0}
472  observations = 0
473 
474 
475  self.convertToLIL()
476 
477  if(verbose >= 2 or self.verbose >= 2): print('DMP dimensions: lanes: '+str(len(self.lane))+' avg.curvy: '+str(sum([len(curv_) for curv_ in self.curvy])/float(len(self.curvy)))+' speed: '+str(len(self.speed))+' userTypes: '+str(len(self.userType))+' timehorizon: '+str(len(self.deltatime))+' x,y: '+str(len(self.xrange))+','+str(len(self.yrange)))
478  if(verbose or self.verbose): prog = tvaLib.ProgressBar(0, len(objects), 77)
479  for i in range(len(objects)):
480 
481  if(verbose or self.verbose): prog.updateAmount(i)
482  cur_userType = objects[i].getUserType()
483  for point in range(len(objects[i].getXCoordinates())):
484  cur_lane = objects[i].curvilinearPositions.getLanes()[point]
485  op_curvy = self.curvy[cur_lane]
486  cur_curvy = op_curvy.findIDofLowerRangeFromValue(objects[i].curvilinearPositions.getXCoordinates()[point])
487  cur_speed = self.speed.findIDofLowerRangeFromValue(objects[i].getSpeeds()[point]*self.speed_conv)
488  for t in range(point+1, len(objects[i].getXCoordinates())):
489  dt = t - point
490  proj_x = self.xrange.findIDofLowerRangeFromValue(objects[i].getXCoordinates()[t])
491  proj_y = self.yrange.findIDofLowerRangeFromValue(objects[i].getYCoordinates()[t])
492  try:
493  self.data[cur_lane][cur_curvy][cur_speed][cur_userType][dt].incData(proj_x, proj_y)
494  observations += 1
495  except Exception:
496  if(objects[i].curvilinearPositions.getLanes()[point] > self.lane.max): skipped['lanes'] += 1
497  elif(objects[i].curvilinearPositions.getXCoordinates()[point] > op_curvy.max): skipped['align'] += 1
498  elif(objects[i].getSpeeds()[point] > self.speed.max): skipped['speed'] += 1
499  elif(objects[i].getUserType() > self.userType.max): skipped['userType'] += 1
500  elif(dt > self.deltatime.max): skipped['time'] += 1
501  elif(objects[i].getXCoordinates()[t] > self.xrange.max): skipped['x'] += 1
502  elif(objects[i].getYCoordinates()[t] > self.yrange.max): skipped['y'] += 1
503  else: skipped['unknown'] += 1
504 
505 
506  self.convertToCSR()
507 
508  if(verbose >= 2 or self.verbose >= 2):
509  print(' -------------')
510  print(' Discretised motion pattern learning: '+str(observations)+' observations; skipped samples: ')
511  for skip in skipped:
512  print(' '+skip+': '+str(skipped[skip])+' ('+str(round(skipped[skip]/float(observations+sum([skipped[x] for x in skipped])), 2)*100.0)+'%)')
513  print(' Total: '+str(sum([skipped[x] for x in skipped]))+' ('+str(round(sum([skipped[x] for x in skipped])/float(observations+sum([skipped[x] for x in skipped])), 2)*100.0)+'%)')
514  if(skipped['unknown']/float(observations+sum([skipped[x] for x in skipped])) > 0.1): tvaLib.printWarning('A majority of samples are being skipped for unknown reasons. Consider investigating the MPA.', indent=4)
515 
516  return True
517 
518  def pruneMotionPattern(self, verbose=0):
519  ''' Delete any cells that do not have sufficient samples for
520  prediction. This process helps to prevent ballooning size of memory
521  footprint for fractured data. Minimum size uses self.minSampleSize
522 
523  Recommend running on newly learned DMP immediatly before storage.
524  '''
525  if(verbose >= 2 or self.verbose >= 2): print('Pruning motion pattern with a minimum of '+str(self.minSampleSize)+' samples...')
526 
527  self.convertToLIL()
528 
529  [[[[[[0 if t[i,j] < self.minSampleSize else t[i,j] for i,j in zip(*t.nonzero())] for t in userType] for userType in speed] for speed in curvy] for curvy in lane] for lane in self.data]
530  self.applyNamedFuncToEachSparse('eliminate_zeros')
531 
532 
533  self.convertToCSR()
534 
535  def convertToCSR(self):
536  if(self.sparse_type == 'sparse_csr'): return False
537  self.applyNamedFuncToEachSparse('convertToType', 'sparse_csr')
538  self.sparse_type = 'sparse_csr'
539  return True
540 
541  def convertToLIL(self):
542  if(self.sparse_type == 'sparse_lil'): return False
543  self.applyNamedFuncToEachSparse('convertToType', 'sparse_lil')
544  self.sparse_type = 'sparse_lil'
545  return True
546 
547  def applyNamedFuncToEachSparse(self, namedFunc, *args, **kwargs):
548  [[[[[getattr(z, namedFunc)(*args, **kwargs) for z in y] for y in x] for x in w] for w in v] for v in self.data]
549  return True
550 
551  def getLargestValAt(self, lane, curvy, speed, userType, time):
552  return np.amax(self.data[lane][curvy][speed][userType][time].getMat())
553 
554  def getSampleSizeAt(self, lane, curvy, speed, userType, time):
555  return np.sum(self.data[lane][curvy][speed][userType][time].getMat())
556 
557  def getSampleSizes(self):
558  return [[[[sum([np.sum(t.getMat()) for t in userType])/float(len(userType)) for userType in speed] for speed in curvy] for curvy in lane] for lane in self.data]
559 
561  return sum(tvaLib.flatten_list([[[[[len(t) for t in userType] for userType in speed] for speed in curvy] for curvy in lane] for lane in self.data]))
562 
563  def getSpatialOrigin(self):
564  ''' Return [xmin, ymin]. '''
565  return [self.xrange[0], self.yrange[0]]
566 
567  def getSpatialBounds(self):
568  ''' Returns [xmin, xmax, ymin, ymax]. '''
569  return [self.xrange[0], self.xrange[-1], self.yrange[0], self.yrange[-1]]
570 
571  def getPointData(self, lane, curvy, speed, userType, time, format='points'):
572  ''' Return a data layer as a formated list of points or columns of x,y
573  format='points' -> [[p1x,p1y],[p2x,p2y],...]
574  format='columns' -> [[p1x,p2x,...],[p1y,p2y,...]] '''
575  data = self.data[lane][curvy][speed][userType][time].getMat()
576  pointList = []
577  for i in range(len(data)):
578  for j in range(len(data[i])):
579  for k in range(int(data[i][j])):
580  pointList.append([self.xrange.getMidRangeValue(i),self.yrange.getMidRangeValue(j)])
581 
582 
583  if(format == 'columns'):
584  return tvaLib.pointList2ColumnList(pointList)
585  else:
586  return pointList
587 
588  def getMatrixData(self, lane, curvy, speed, userType, time, format='2D-list', normalise=False):
589  ''' Return a data layer as a matrix formated as a 2D list
590  format='2D-list' -> [[r1c1,r1c2,...],[r2c1,r2c2,...],...]
591  format='2D-array' -> array([[r1c1,r1c2,...],[r2c1,r2c2,...],...]) '''
592  try:
593  data = self.data[lane][curvy][speed][userType][time].getMat()
594  except Exception:
595  return []
596  if(normalise):
597  normal_sum = np.sum(data)
598  if(normal_sum > 0):
599  data = data / normal_sum
600  if(format=='2D-array'): return data
601  else:
602  matrix = []
603  for i in data:
604  matrix.append(list(i))
605  return matrix
606 
607  def getDataSampleSize(self, lane, curvy, speed, userType, time):
608  ''' Return sample size of a data layer. '''
609  return len(self.data[lane][curvy][speed][userType][time])
610 
611  def testDMPcells(self, skip_debugger=False):
612  ''' inject this function during debugging to find and inspect code. '''
613  for lane in range(len(self.data)):
614  for curvy in range(len(self.data[lane])):
615  for speed in range(len(self.data[lane][curvy])):
616  for userType in range(len(self.data[lane][curvy][speed])):
617  for time in range(len(self.data[lane][curvy][speed][userType])):
618  if(len(self.data[lane][curvy][speed][userType][time])):
619  if(not skip_debugger): import pdb; pdb.set_trace()
620  return (lane, curvy, speed, userType, time)
621  return False
def convertToType(self, dtype)
Definition: int.py:1
def pruneMotionPattern(self, verbose=0)
def prepare(self, objects=False, allowImport=True, importLocations='', customSpeedRange=None, version='', verbose=0, kwargs)
def getLargestValAt(self, lane, curvy, speed, userType, time)
def genRange(self, observations=None)
def export(self, path=None, version=None, kwargs)
def __init__(self, alignments, xy_bounds, userTypes=7, maxSpeed=80, speed_conv=54, objects=None, timeHorizon=None, speedBinNum=7, curvilinearBinSize=1, motPatBinNum=100, probRetentionThreshold=0.00001, minSampleSize=50, motPatBinSize=[1, verbose=0, kwargs)
def importDMP(self, paths, version=None, filename='dmp.mp', verificationDepth=3, verbose=0)
def applyNamedFuncToEachSparse(self, namedFunc, args, kwargs)
def getDataSampleSize(self, lane, curvy, speed, userType, time)
verbose
Self-generate bounds.
def getSampleSizeAt(self, lane, curvy, speed, userType, time)
def setData(self, x, y, value)
def incData(self, x, y, value=1)
def __init__(self, dtype='sparse_csr')
def learnMotionPattern(self, objects, customSpeedRange=None, verbose=0)
def __init__(self, min, max, step=0, steps=0, observations=None)
def computeCrossingsCollisions(self, obj1, obj2, interval, triage=False, scatterReturnPosition=False, VolumetricCollisionDistanceThreshold=[2, timeHorizon=100, verbose=0)
def multiply(self, target)
def getPointData(self, lane, curvy, speed, userType, time, format='points')
def getMidRangeValue(self, index)
def getMatrixData(self, lane, curvy, speed, userType, time, format='2D-list', normalise=False)
def exportDMP(self, path, version=None, filename='dmp.mp', kwargs)
def findIDofLowerRangeFromValue(self, value)
def testDMPcells(self, skip_debugger=False)