comparison scripts/process.py @ 1161:b968c33f8c2f

work on producing figures and collision maps for large datasets with multiple sites
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Thu, 04 Mar 2021 23:12:34 -0500
parents 8734742c08c0
children
comparison
equal deleted inserted replaced
1160:3a6b00f13e95 1161:b968c33f8c2f
22 # main function 22 # main function
23 parser.add_argument('--delete', dest = 'delete', help = 'data to delete', choices = ['feature', 'object', 'classification', 'interaction']) 23 parser.add_argument('--delete', dest = 'delete', help = 'data to delete', choices = ['feature', 'object', 'classification', 'interaction'])
24 parser.add_argument('--process', dest = 'process', help = 'data to process', choices = ['feature', 'object', 'classification', 'prototype', 'interaction']) 24 parser.add_argument('--process', dest = 'process', help = 'data to process', choices = ['feature', 'object', 'classification', 'prototype', 'interaction'])
25 parser.add_argument('--display', dest = 'display', help = 'data to display (replay over video)', choices = ['feature', 'object', 'classification', 'interaction']) 25 parser.add_argument('--display', dest = 'display', help = 'data to display (replay over video)', choices = ['feature', 'object', 'classification', 'interaction'])
26 parser.add_argument('--progress', dest = 'progress', help = 'information about the progress of processing', action = 'store_true') 26 parser.add_argument('--progress', dest = 'progress', help = 'information about the progress of processing', action = 'store_true')
27 parser.add_argument('--analyze', dest = 'analyze', help = 'data to analyze (results)', choices = ['feature', 'object', 'classification', 'interaction', 'event-speed', 'event-interaction']) 27 parser.add_argument('--analyze', dest = 'analyze', help = 'data to analyze (results)', choices = ['feature', 'object', 'classification', 'interaction', 'collision-map', 'event-speed', 'event-interaction'])
28 28
29 # common options 29 # common options
30 parser.add_argument('--cfg', dest = 'configFilename', help = 'name of the configuration file') 30 parser.add_argument('--cfg', dest = 'configFilename', help = 'name of the configuration file')
31 parser.add_argument('-n', dest = 'nObjects', help = 'number of objects/interactions to process', type = int) 31 parser.add_argument('-n', dest = 'nObjects', help = 'number of objects/interactions to process', type = int)
32 parser.add_argument('-t', dest = 'trajectoryType', help = 'type of trajectories', choices = ['feature', 'object'], default = 'feature') 32 parser.add_argument('-t', dest = 'trajectoryType', help = 'type of trajectories', choices = ['feature', 'object'], default = 'feature')
59 59
60 60
61 # analysis options 61 # analysis options
62 parser.add_argument('--output', dest = 'output', help = 'kind of output to produce (interval means)', choices = ['figure', 'interval', 'event']) 62 parser.add_argument('--output', dest = 'output', help = 'kind of output to produce (interval means)', choices = ['figure', 'interval', 'event'])
63 parser.add_argument('--min-duration', dest = 'minDuration', help = 'mininum duration we have to see the user or interaction to take into account in the analysis (s)', type = float) 63 parser.add_argument('--min-duration', dest = 'minDuration', help = 'mininum duration we have to see the user or interaction to take into account in the analysis (s)', type = float)
64 parser.add_argument('--max-time-indicator-value', dest = 'maxTimeIndicatorValue', help = 'maximum indicator value for time indicators like PET and TTC (s)', type = float)
65 parser.add_argument('--max-speed-indicator-value', dest = 'maxSpeedIndicatorValue', help = 'maximum indicator value for speed indicators like individual speed statistics and speed differential (km/h)', type = float)
64 parser.add_argument('--interval-duration', dest = 'intervalDuration', help = 'length of time interval to aggregate data (min)', type = int, default = 15) 66 parser.add_argument('--interval-duration', dest = 'intervalDuration', help = 'length of time interval to aggregate data (min)', type = int, default = 15)
65 parser.add_argument('--aggregation', dest = 'aggMethods', help = 'aggregation method per user/interaction and per interval', choices = ['mean', 'median', 'centile'], nargs = '*', default = ['median']) 67 parser.add_argument('--aggregation', dest = 'aggMethods', help = 'aggregation method per user/interaction and per interval', choices = ['mean', 'median', 'centile'], nargs = '*', default = ['median'])
66 parser.add_argument('--aggregation-centiles', dest = 'aggCentiles', help = 'centile(s) to compute from the observations', nargs = '*', type = int) 68 parser.add_argument('--aggregation-centiles', dest = 'aggCentiles', help = 'centile(s) to compute from the observations', nargs = '*', type = int)
67 parser.add_argument('--event-thresholds', dest = 'eventThresholds', help = 'threshold to count severe situations', nargs = '*', type = float) 69 parser.add_argument('--event-thresholds', dest = 'eventThresholds', help = 'threshold to count severe situations', nargs = '*', type = float)
68 parser.add_argument('--event-filename', dest = 'eventFilename', help = 'filename of the event data') 70 parser.add_argument('--event-filename', dest = 'eventFilename', help = 'filename of the event data')
136 print('-'*80) 138 print('-'*80)
137 if len(videoSequences) > 0: 139 if len(videoSequences) > 0:
138 print('{} video sequences without a camera view:'.format(len(videoSequences))) 140 print('{} video sequences without a camera view:'.format(len(videoSequences)))
139 print([vs.idx for vs in videoSequences]) 141 print([vs.idx for vs in videoSequences])
140 print('-'*80) 142 print('-'*80)
141 print(data) 143 with pd.option_context('display.max_rows', None, 'display.max_columns', None):
144 print(data)
142 145
143 ################################# 146 #################################
144 # Delete 147 # Delete
145 ################################# 148 #################################
146 if args.delete is not None: 149 if args.delete is not None:
282 elif args.output == 'event': 285 elif args.output == 'event':
283 data.to_csv(args.eventFilename, index = False) 286 data.to_csv(args.eventFilename, index = False)
284 287
285 if args.analyze == 'interaction': # redo as for object, export in dataframe all interaction data 288 if args.analyze == 'interaction': # redo as for object, export in dataframe all interaction data
286 indicatorIds = [2,5,7,10] 289 indicatorIds = [2,5,7,10]
287 conversionFactors = {2: 1., 5: 30.*3.6, 7:1./30, 10:1./30}
288 #maxIndicatorValue = {2: float('inf'), 5: float('inf'), 7:10., 10:10.} 290 #maxIndicatorValue = {2: float('inf'), 5: float('inf'), 7:10., 10:10.}
289 data = [] # list of observation per site-user with time 291 data = [] # list of observation per site-user with time
290 headers = ['site', 'date', 'time', events.Interaction.indicatorNames[10].replace(' ','-')] # user types? 292 headers = ['site', 'date', 'time', events.Interaction.indicatorNames[10].replace(' ','-')] # user types?
291 aggFunctions, tmpheaders = utils.aggregationMethods(args.aggMethods, args.aggCentiles) 293 aggFunctions, tmpheaders = utils.aggregationMethods(args.aggMethods, args.aggCentiles)
294 nAggFunctions = len(tmpheaders)
295 indicatorUnits = [events.Interaction.indicatorUnits[10]] # for PET above
292 for i in indicatorIds[:3]: 296 for i in indicatorIds[:3]:
293 for h in tmpheaders: 297 for h in tmpheaders:
294 headers.append(events.Interaction.indicatorNames[i].replace(' ','-')+'-'+h) 298 headers.append(events.Interaction.indicatorNames[i].replace(' ','-')+'-'+h)
295 indicators = {} 299 indicatorUnits.append(events.Interaction.indicatorUnits[i])
296 interactions = {}
297 for vs in videoSequences: 300 for vs in videoSequences:
298 print('Extracting SMoS from '+vs.getDatabaseFilename()) 301 print('Extracting SMoS from '+vs.getDatabaseFilename())
299 interactions = storage.loadInteractionsFromSqlite(str(parentPath/vs.getDatabaseFilename())) 302 interactions = storage.loadInteractionsFromSqlite(str(parentPath/vs.getDatabaseFilename()))
300 minDuration = vs.cameraView.cameraType.frameRate*args.minDuration 303 minDuration = vs.cameraView.cameraType.frameRate*args.minDuration
304 conversionFactors = {2: 1., 5: 3.6*vs.cameraView.cameraType.frameRate, 7:1./vs.cameraView.cameraType.frameRate, 10:1./vs.cameraView.cameraType.frameRate}
301 for inter in interactions: 305 for inter in interactions:
302 if inter.length() > minDuration: 306 if inter.length() > minDuration:
303 d = vs.startTime.date() 307 d = vs.startTime.date()
304 t = vs.startTime.time() 308 t = vs.startTime.time()
305 row = [vs.cameraView.site.name, d, utils.framesToTime(inter.getFirstInstant(), vs.cameraView.cameraType.frameRate, t)] 309 row = [vs.cameraView.site.name, d, utils.framesToTime(inter.getFirstInstant(), vs.cameraView.cameraType.frameRate, t)]
306 pet = inter.getIndicator('Post Encroachment Time') 310 pet = inter.getIndicator(events.Interaction.indicatorNames[10])
307 if pet is None: 311 if pet is None:
308 row.append(None) 312 row.append(None)
309 else: 313 else:
310 row.append(conversionFactors[10]*pet.getValues()[0]) 314 row.append(conversionFactors[10]*pet.getValues()[0])
311 for i in indicatorIds[:3]: 315 for i in indicatorIds[:3]:
318 if method == 'centile': 322 if method == 'centile':
319 row.extend(agg.tolist()) 323 row.extend(agg.tolist())
320 else: 324 else:
321 row.append(agg) 325 row.append(agg)
322 else: 326 else:
323 row.extend([None]*len(aggFunctions)) 327 row.extend([None]*nAggFunctions)
324 data.append(row) 328 data.append(row)
325 data = pd.DataFrame(data, columns = headers) 329 data = pd.DataFrame(data, columns = headers)
326 if args.output == 'figure': 330 if args.output == 'figure':
327 for i in indicatorIds: 331 plt.ioff()
328 pass # tmp = [indicators[siteId][i] for siteId in indicators] 332 for i, indic in enumerate(headers[3:]):
329 # plt.ioff() 333 if 'Time' in indic and args.maxTimeIndicatorValue is not None:
330 # plt.figure() 334 tmp = data.loc[data[indic] < args.maxTimeIndicatorValue, ['site', indic]]
335 elif 'Speed' in indic and args.maxSpeedIndicatorValue is not None:
336 tmp = data.loc[data[indic] < args.maxSpeedIndicatorValue, ['site', indic]]
337 else:
338 tmp = data[['site', indic]]
339 plt.figure()
340 tmp.boxplot(indic, 'site')
331 # plt.boxplot(tmp, labels = [session.query(Site).get(siteId).name for siteId in indicators]) 341 # plt.boxplot(tmp, labels = [session.query(Site).get(siteId).name for siteId in indicators])
332 # plt.ylabel(events.Interaction.indicatorNames[i]+' ('+events.Interaction.indicatorUnits[i]+')') 342 plt.ylabel(indic+' ('+indicatorUnits[i]+')')
333 # plt.savefig(events.Interaction.indicatorNames[i]+'.png', dpi=150) 343 plt.savefig('boxplot-sites-'+indic+'.pdf')#, dpi=150)
334 # plt.close() 344 plt.close()
345 plt.figure()
346 for site in sorted(tmp.site.unique()):
347 x = sorted(tmp.loc[tmp.site == site, indic])
348 plt.plot(x, np.arange(1,len(x)+1)/len(x), label=site)
349 plt.legend()
350 plt.title('Cumulative Distribution Function by Site')
351 plt.xlabel(indic+' ('+indicatorUnits[i]+')')
352 plt.savefig('cdf-sites-'+indic+'.pdf')
353 plt.close()
335 elif args.output == 'event': 354 elif args.output == 'event':
336 data.to_csv(args.eventFilename, index = False) 355 data.to_csv(args.eventFilename, index = False)
337 356
357 if args.analyze == 'collision-map':
358 predictionParameters = prediction.CVExactPredictionParameters()
359 data = []
360 for vs in videoSequences:
361 print('Extracting potential collision points from '+vs.getDatabaseFilename())
362 interactions = storage.loadInteractionsFromSqlite(str(parentPath/vs.getDatabaseFilename()))
363 objects = storage.loadTrajectoriesFromSqlite(str(parentPath/vs.getDatabaseFilename()), 'object')
364 params = storage.ProcessParameters(str(parentPath/vs.cameraView.getTrackingConfigurationFilename()))
365 minDuration = vs.cameraView.cameraType.frameRate*args.minDuration
366 maxTimeIndicatorValue = vs.cameraView.cameraType.frameRate*args.maxTimeIndicatorValue
367 for inter in interactions:
368 if inter.length() > minDuration:
369 ttc = inter.getIndicator(events.Interaction.indicatorNames[7])
370 if ttc is not None:
371 t = min(ttc.values, key = ttc.values.get)
372 if args.maxTimeIndicatorValue is None or ttc.values[t] < maxTimeIndicatorValue:
373 inter.setRoadUsers(objects)
374 cps, _ = predictionParameters.computeCrossingsCollisionsAtInstant(t, inter.roadUser1, inter.roadUser2, params.collisionDistance, params.predictionTimeHorizon)
375 data.append([vs.cameraView.site.name, cps[0].x, cps[0].y, cps[0].indicator])
376 data = pd.DataFrame(data, columns = ['site', 'x', 'y', 'ttc'])
377 margin = 0.1
378 for site in data.site.unique():
379 s = session.query(Site).filter(Site.name.like('%'+site+'%')).first()
380 img = plt.imread(str(parentPath/s.getMapImageFilename()))
381 tmp = data[data.site == site].copy()
382 tmp.x = tmp.x/s.nUnitsPerPixel
383 tmp.y = tmp.y/s.nUnitsPerPixel
384 h, w, _ = img.shape
385 tmp = tmp[(tmp.x>-margin*w) & (tmp.x < (1+margin)*w) & (tmp.y > -margin*h) & (tmp.y < (1+margin)*h)]
386 plt.figure()
387 plt.imshow(img)
388 plt.hexbin(tmp.x, tmp.y, alpha = 0.5, edgecolors = 'face', mincnt=1, gridsize=50)
389 plt.title('Density of Potential Collision Points at Site '+site)
390 plt.colorbar()
391 plt.axis('equal')
392 plt.savefig('collision-map-'+site+'.pdf')
393 #plt.close()
394
338 if args.analyze == 'event-speed': # aggregate event data by 15 min interval (args.intervalDuration), count events with thresholds 395 if args.analyze == 'event-speed': # aggregate event data by 15 min interval (args.intervalDuration), count events with thresholds
339 data = pd.read_csv(args.eventFilename, parse_dates = [2]) 396 data = pd.read_csv(args.eventFilename, parse_dates = [2])
340 #data = pd.read_csv('./speeds.csv', converters = {'time': lambda s: datetime.datetime.strptime(s, "%H:%M:%S").time()}, nrows = 5000) 397 #data = pd.read_csv('./speeds.csv', converters = {'time': lambda s: datetime.datetime.strptime(s, "%H:%M:%S").time()}, nrows = 5000)
341 # create time for end of each 15 min, then group by, using the agg method for each data column 398 # create time for end of each 15 min, then group by, using the agg method for each data column
342 headers = ['site', 'date', 'intervalend15', 'duration', 'count'] 399 headers = ['site', 'date', 'intervalend15', 'duration', 'count']
375 432
376 elif args.analyze == 'event-interaction': # aggregate event data by 15 min interval (args.intervalDuration), count events with thresholds 433 elif args.analyze == 'event-interaction': # aggregate event data by 15 min interval (args.intervalDuration), count events with thresholds
377 data = pd.read_csv(args.eventFilename, parse_dates = [2]) 434 data = pd.read_csv(args.eventFilename, parse_dates = [2])
378 headers = ['site', 'date', 'intervalend15', 'duration', 'count'] 435 headers = ['site', 'date', 'intervalend15', 'duration', 'count']
379 aggFunctions, tmpheaders = utils.aggregationMethods(args.aggMethods, args.aggCentiles) 436 aggFunctions, tmpheaders = utils.aggregationMethods(args.aggMethods, args.aggCentiles)
437 nAggFunctions = len(tmpheaders)
380 dataColumns = list(data.columns[3:]) 438 dataColumns = list(data.columns[3:])
381 for h in dataColumns: 439 for h in dataColumns:
382 if not 'speed' in h.lower(): # proximity indicators are reversed, taking 85th centile of this column will yield the 15th centile (which we have to take the opposite again) 440 if not 'speed' in h.lower(): # proximity indicators are reversed, taking 85th centile of this column will yield the 15th centile (which we have to take the opposite again)
383 data[h] = -data[h] 441 data[h] = -data[h]
384 for h in dataColumns: 442 for h in dataColumns:
403 if method == 'centile': 461 if method == 'centile':
404 row.extend(np.abs(aggregated)) 462 row.extend(np.abs(aggregated))
405 else: 463 else:
406 row.append(np.abs(aggregated)) 464 row.append(np.abs(aggregated))
407 else: 465 else:
408 row.extend([None]*len(aggFunctions)) 466 row.extend([None]*nAggFunctions)
409 for h,t in zip(dataColumns, args.eventThresholds): # each threshold in this case applies to one indicator 467 for h,t in zip(dataColumns, args.eventThresholds): # each threshold in this case applies to one indicator
410 if 'speed' in h.lower(): 468 if 'speed' in h.lower():
411 row.append((group[h] > t).sum()) 469 row.append((group[h] > t).sum())
412 else: 470 else:
413 row.append((group[h] > -t).sum()) # take larger than than negative threshold for proximity indicators 471 row.append((group[h] > -t).sum()) # take larger than than negative threshold for proximity indicators