Mercurial > hg > nsaunier > traffic-intelligence
comparison trafficintelligence/storage.py @ 1028:cc5cb04b04b0
major update using the trafficintelligence package name and install through pip
| author | Nicolas Saunier <nicolas.saunier@polymtl.ca> |
|---|---|
| date | Fri, 15 Jun 2018 11:19:10 -0400 |
| parents | python/storage.py@73b124160911 |
| children | aafbc0bab925 |
comparison
equal
deleted
inserted
replaced
| 1027:6129296848d3 | 1028:cc5cb04b04b0 |
|---|---|
| 1 #! /usr/bin/env python | |
| 2 # -*- coding: utf-8 -*- | |
| 3 '''Various utilities to save and load data''' | |
| 4 | |
| 5 from trafficintelligence import utils, moving, events, indicators | |
| 6 from trafficintelligence.base import VideoFilenameAddable | |
| 7 | |
| 8 from pathlib import Path | |
| 9 import shutil | |
| 10 from copy import copy | |
| 11 import sqlite3, logging | |
| 12 from numpy import log, min as npmin, max as npmax, round as npround, array, sum as npsum, loadtxt, floor as npfloor, ceil as npceil, linalg | |
| 13 from pandas import read_csv, merge | |
| 14 | |
| 15 | |
| 16 commentChar = '#' | |
| 17 | |
| 18 delimiterChar = '%'; | |
| 19 | |
| 20 ngsimUserTypes = {'twowheels':1, | |
| 21 'car':2, | |
| 22 'truck':3} | |
| 23 | |
| 24 tableNames = {'feature':'positions', | |
| 25 'object': 'objects', | |
| 26 'objectfeatures': 'positions'} | |
| 27 | |
| 28 ######################### | |
| 29 # Sqlite | |
| 30 ######################### | |
| 31 | |
| 32 # utils | |
| 33 def printDBError(error): | |
| 34 print('DB Error: {}'.format(error)) | |
| 35 | |
| 36 def dropTables(connection, tableNames): | |
| 37 'deletes the table with names in tableNames' | |
| 38 try: | |
| 39 cursor = connection.cursor() | |
| 40 for tableName in tableNames: | |
| 41 cursor.execute('DROP TABLE IF EXISTS '+tableName) | |
| 42 except sqlite3.OperationalError as error: | |
| 43 printDBError(error) | |
| 44 | |
| 45 def deleteFromSqlite(filename, dataType): | |
| 46 'Deletes (drops) some tables in the filename depending on type of data' | |
| 47 if Path(filename).is_file(): | |
| 48 with sqlite3.connect(filename) as connection: | |
| 49 if dataType == 'object': | |
| 50 dropTables(connection, ['objects', 'objects_features']) | |
| 51 elif dataType == 'interaction': | |
| 52 dropTables(connection, ['interactions', 'indicators']) | |
| 53 elif dataType == 'bb': | |
| 54 dropTables(connection, ['bounding_boxes']) | |
| 55 elif dataType == 'pois': | |
| 56 dropTables(connection, ['gaussians2d', 'objects_pois']) | |
| 57 elif dataType == 'prototype': | |
| 58 dropTables(connection, ['prototypes', 'objects_prototypes']) | |
| 59 else: | |
| 60 print('Unknown data type {} to delete from database'.format(dataType)) | |
| 61 else: | |
| 62 print('{} does not exist'.format(filename)) | |
| 63 | |
| 64 def tableExists(connection, tableName): | |
| 65 'indicates if the table exists in the database' | |
| 66 try: | |
| 67 cursor = connection.cursor() | |
| 68 cursor.execute('SELECT COUNT(*) FROM SQLITE_MASTER WHERE type = \'table\' AND name = \''+tableName+'\'') | |
| 69 return cursor.fetchone()[0] == 1 | |
| 70 except sqlite3.OperationalError as error: | |
| 71 printDBError(error) | |
| 72 | |
| 73 def createTrajectoryTable(cursor, tableName): | |
| 74 if tableName.endswith('positions') or tableName.endswith('velocities'): | |
| 75 cursor.execute("CREATE TABLE IF NOT EXISTS "+tableName+" (trajectory_id INTEGER, frame_number INTEGER, x_coordinate REAL, y_coordinate REAL, PRIMARY KEY(trajectory_id, frame_number))") | |
| 76 else: | |
| 77 print('Unallowed name {} for trajectory table'.format(tableName)) | |
| 78 | |
| 79 def createObjectsTable(cursor): | |
| 80 cursor.execute("CREATE TABLE IF NOT EXISTS objects (object_id INTEGER, road_user_type INTEGER, n_objects INTEGER, PRIMARY KEY(object_id))") | |
| 81 | |
| 82 def createAssignmentTable(cursor, objectType1, objectType2, objectIdColumnName1, objectIdColumnName2): | |
| 83 cursor.execute("CREATE TABLE IF NOT EXISTS "+objectType1+"s_"+objectType2+"s ("+objectIdColumnName1+" INTEGER, "+objectIdColumnName2+" INTEGER, PRIMARY KEY("+objectIdColumnName1+","+objectIdColumnName2+"))") | |
| 84 | |
| 85 def createObjectsFeaturesTable(cursor): | |
| 86 cursor.execute("CREATE TABLE IF NOT EXISTS objects_features (object_id INTEGER, trajectory_id INTEGER, PRIMARY KEY(object_id, trajectory_id))") | |
| 87 | |
| 88 | |
| 89 def createCurvilinearTrajectoryTable(cursor): | |
| 90 cursor.execute("CREATE TABLE IF NOT EXISTS curvilinear_positions (trajectory_id INTEGER, frame_number INTEGER, s_coordinate REAL, y_coordinate REAL, lane TEXT, PRIMARY KEY(trajectory_id, frame_number))") | |
| 91 | |
| 92 def createFeatureCorrespondenceTable(cursor): | |
| 93 cursor.execute('CREATE TABLE IF NOT EXISTS feature_correspondences (trajectory_id INTEGER, source_dbname VARCHAR, db_trajectory_id INTEGER, PRIMARY KEY(trajectory_id))') | |
| 94 | |
| 95 def createInteractionTable(cursor): | |
| 96 cursor.execute('CREATE TABLE IF NOT EXISTS interactions (id INTEGER PRIMARY KEY, object_id1 INTEGER, object_id2 INTEGER, first_frame_number INTEGER, last_frame_number INTEGER, FOREIGN KEY(object_id1) REFERENCES objects(id), FOREIGN KEY(object_id2) REFERENCES objects(id))') | |
| 97 | |
| 98 def createIndicatorTable(cursor): | |
| 99 cursor.execute('CREATE TABLE IF NOT EXISTS indicators (interaction_id INTEGER, indicator_type INTEGER, frame_number INTEGER, value REAL, FOREIGN KEY(interaction_id) REFERENCES interactions(id), PRIMARY KEY(interaction_id, indicator_type, frame_number))') | |
| 100 | |
| 101 def insertTrajectoryQuery(tableName): | |
| 102 return "INSERT INTO "+tableName+" VALUES (?,?,?,?)" | |
| 103 | |
| 104 def insertObjectQuery(): | |
| 105 return "INSERT INTO objects VALUES (?,?,?)" | |
| 106 | |
| 107 def insertObjectFeatureQuery(): | |
| 108 return "INSERT INTO objects_features VALUES (?,?)" | |
| 109 | |
| 110 def createIndex(connection, tableName, columnName, unique = False): | |
| 111 '''Creates an index for the column in the table | |
| 112 I will make querying with a condition on this column faster''' | |
| 113 try: | |
| 114 cursor = connection.cursor() | |
| 115 s = "CREATE " | |
| 116 if unique: | |
| 117 s += "UNIQUE " | |
| 118 cursor.execute(s+"INDEX IF NOT EXISTS "+tableName+"_"+columnName+"_index ON "+tableName+"("+columnName+")") | |
| 119 connection.commit() | |
| 120 #connection.close() | |
| 121 except sqlite3.OperationalError as error: | |
| 122 printDBError(error) | |
| 123 | |
| 124 def getNumberRowsTable(connection, tableName, columnName = None): | |
| 125 '''Returns the number of rows for the table | |
| 126 If columnName is not None, means we want the number of distinct values for that column | |
| 127 (otherwise, we can just count(*))''' | |
| 128 try: | |
| 129 cursor = connection.cursor() | |
| 130 if columnName is None: | |
| 131 cursor.execute("SELECT COUNT(*) from "+tableName) | |
| 132 else: | |
| 133 cursor.execute("SELECT COUNT(DISTINCT "+columnName+") from "+tableName) | |
| 134 return cursor.fetchone()[0] | |
| 135 except sqlite3.OperationalError as error: | |
| 136 printDBError(error) | |
| 137 | |
| 138 def getMinMax(connection, tableName, columnName, minmax): | |
| 139 '''Returns max/min or both for given column in table | |
| 140 minmax must be string max, min or minmax''' | |
| 141 try: | |
| 142 cursor = connection.cursor() | |
| 143 if minmax == 'min' or minmax == 'max': | |
| 144 cursor.execute("SELECT "+minmax+"("+columnName+") from "+tableName) | |
| 145 elif minmax == 'minmax': | |
| 146 cursor.execute("SELECT MIN("+columnName+"), MAX("+columnName+") from "+tableName) | |
| 147 else: | |
| 148 print("Argument minmax unknown: {}".format(minmax)) | |
| 149 return cursor.fetchone()[0] | |
| 150 except sqlite3.OperationalError as error: | |
| 151 printDBError(error) | |
| 152 | |
| 153 def getObjectCriteria(objectNumbers): | |
| 154 if objectNumbers is None: | |
| 155 query = '' | |
| 156 elif type(objectNumbers) == int: | |
| 157 query = '<= {0}'.format(objectNumbers-1) | |
| 158 elif type(objectNumbers) == list: | |
| 159 query = 'in ('+', '.join([str(n) for n in objectNumbers])+')' | |
| 160 else: | |
| 161 print('objectNumbers {} are not a known type ({})'.format(objectNumbers, type(objectNumbers))) | |
| 162 query = '' | |
| 163 return query | |
| 164 | |
| 165 def loadTrajectoriesFromTable(connection, tableName, trajectoryType, objectNumbers = None, timeStep = None): | |
| 166 '''Loads trajectories (in the general sense) from the given table | |
| 167 can be positions or velocities | |
| 168 | |
| 169 returns a moving object''' | |
| 170 cursor = connection.cursor() | |
| 171 | |
| 172 try: | |
| 173 objectCriteria = getObjectCriteria(objectNumbers) | |
| 174 queryStatement = None | |
| 175 if trajectoryType == 'feature': | |
| 176 queryStatement = 'SELECT * from '+tableName | |
| 177 if objectNumbers is not None and timeStep is not None: | |
| 178 queryStatement += ' WHERE trajectory_id '+objectCriteria+' AND frame_number%{} = 0'.format(timeStep) | |
| 179 elif objectNumbers is not None: | |
| 180 queryStatement += ' WHERE trajectory_id '+objectCriteria | |
| 181 elif timeStep is not None: | |
| 182 queryStatement += ' WHERE frame_number%{} = 0'.format(timeStep) | |
| 183 queryStatement += ' ORDER BY trajectory_id, frame_number' | |
| 184 elif trajectoryType == 'object': | |
| 185 queryStatement = 'SELECT OF.object_id, P.frame_number, avg(P.x_coordinate), avg(P.y_coordinate) from '+tableName+' P, objects_features OF WHERE P.trajectory_id = OF.trajectory_id' | |
| 186 if objectNumbers is not None: | |
| 187 queryStatement += ' AND OF.object_id '+objectCriteria | |
| 188 if timeStep is not None: | |
| 189 queryStatement += ' AND P.frame_number%{} = 0'.format(timeStep) | |
| 190 queryStatement += ' GROUP BY OF.object_id, P.frame_number ORDER BY OF.object_id, P.frame_number' | |
| 191 elif trajectoryType in ['bbtop', 'bbbottom']: | |
| 192 if trajectoryType == 'bbtop': | |
| 193 corner = 'top_left' | |
| 194 elif trajectoryType == 'bbbottom': | |
| 195 corner = 'bottom_right' | |
| 196 queryStatement = 'SELECT object_id, frame_number, x_'+corner+', y_'+corner+' FROM '+tableName | |
| 197 if objectNumbers is not None and timeStep is not None: | |
| 198 queryStatement += ' WHERE object_id '+objectCriteria+' AND frame_number%{} = 0'.format(timeStep) | |
| 199 elif objectNumbers is not None: | |
| 200 queryStatement += ' WHERE object_id '+objectCriteria | |
| 201 elif timeStep is not None: | |
| 202 queryStatement += ' WHERE frame_number%{} = 0'.format(timeStep) | |
| 203 queryStatement += ' ORDER BY object_id, frame_number' | |
| 204 else: | |
| 205 print('Unknown trajectory type {}'.format(trajectoryType)) | |
| 206 if queryStatement is not None: | |
| 207 cursor.execute(queryStatement) | |
| 208 logging.debug(queryStatement) | |
| 209 except sqlite3.OperationalError as error: | |
| 210 printDBError(error) | |
| 211 return [] | |
| 212 | |
| 213 objId = -1 | |
| 214 obj = None | |
| 215 objects = [] | |
| 216 for row in cursor: | |
| 217 if row[0] != objId: | |
| 218 objId = row[0] | |
| 219 if obj is not None and (obj.length() == obj.positions.length() or (timeStep is not None and npceil(obj.length()/timeStep) == obj.positions.length())): | |
| 220 objects.append(obj) | |
| 221 elif obj is not None: | |
| 222 print('Object {} is missing {} positions'.format(obj.getNum(), int(obj.length())-obj.positions.length())) | |
| 223 obj = moving.MovingObject(row[0], timeInterval = moving.TimeInterval(row[1], row[1]), positions = moving.Trajectory([[row[2]],[row[3]]])) | |
| 224 else: | |
| 225 obj.timeInterval.last = row[1] | |
| 226 obj.positions.addPositionXY(row[2],row[3]) | |
| 227 | |
| 228 if obj is not None and (obj.length() == obj.positions.length() or (timeStep is not None and npceil(obj.length()/timeStep) == obj.positions.length())): | |
| 229 objects.append(obj) | |
| 230 elif obj is not None: | |
| 231 print('Object {} is missing {} positions'.format(obj.getNum(), int(obj.length())-obj.positions.length())) | |
| 232 | |
| 233 return objects | |
| 234 | |
| 235 def loadUserTypesFromTable(cursor, objectNumbers): | |
| 236 objectCriteria = getObjectCriteria(objectNumbers) | |
| 237 queryStatement = 'SELECT object_id, road_user_type FROM objects' | |
| 238 if objectNumbers is not None: | |
| 239 queryStatement += ' WHERE object_id '+objectCriteria | |
| 240 cursor.execute(queryStatement) | |
| 241 userTypes = {} | |
| 242 for row in cursor: | |
| 243 userTypes[row[0]] = row[1] | |
| 244 return userTypes | |
| 245 | |
| 246 def loadTrajectoriesFromSqlite(filename, trajectoryType, objectNumbers = None, withFeatures = False, timeStep = None, tablePrefix = None): | |
| 247 '''Loads the trajectories (in the general sense, | |
| 248 either features, objects (feature groups) or bounding box series) | |
| 249 The number loaded is either the first objectNumbers objects, | |
| 250 or the indices in objectNumbers from the database''' | |
| 251 objects = [] | |
| 252 with sqlite3.connect(filename) as connection: | |
| 253 if tablePrefix is None: | |
| 254 prefix = '' | |
| 255 else: | |
| 256 prefix = tablePrefix + '_' | |
| 257 objects = loadTrajectoriesFromTable(connection, prefix+'positions', trajectoryType, objectNumbers, timeStep) | |
| 258 objectVelocities = loadTrajectoriesFromTable(connection, prefix+'velocities', trajectoryType, objectNumbers, timeStep) | |
| 259 | |
| 260 if len(objectVelocities) > 0: | |
| 261 for o,v in zip(objects, objectVelocities): | |
| 262 if o.getNum() == v.getNum(): | |
| 263 o.velocities = v.positions | |
| 264 o.velocities.duplicateLastPosition() # avoid having velocity shorter by one position than positions | |
| 265 else: | |
| 266 print('Could not match positions {0} with velocities {1}'.format(o.getNum(), v.getNum())) | |
| 267 | |
| 268 if trajectoryType == 'object': | |
| 269 cursor = connection.cursor() | |
| 270 try: | |
| 271 # attribute feature numbers to objects | |
| 272 queryStatement = 'SELECT trajectory_id, object_id FROM objects_features' | |
| 273 if objectNumbers is not None: | |
| 274 queryStatement += ' WHERE object_id '+getObjectCriteria(objectNumbers) | |
| 275 queryStatement += ' ORDER BY object_id' # order is important to group all features per object | |
| 276 logging.debug(queryStatement) | |
| 277 cursor.execute(queryStatement) | |
| 278 | |
| 279 featureNumbers = {} | |
| 280 for row in cursor: | |
| 281 objId = row[1] | |
| 282 if objId not in featureNumbers: | |
| 283 featureNumbers[objId] = [row[0]] | |
| 284 else: | |
| 285 featureNumbers[objId].append(row[0]) | |
| 286 | |
| 287 for obj in objects: | |
| 288 obj.featureNumbers = featureNumbers[obj.getNum()] | |
| 289 | |
| 290 # load userType | |
| 291 userTypes = loadUserTypesFromTable(cursor, objectNumbers) | |
| 292 for obj in objects: | |
| 293 obj.userType = userTypes[obj.getNum()] | |
| 294 | |
| 295 if withFeatures: | |
| 296 nFeatures = 0 | |
| 297 for obj in objects: | |
| 298 nFeatures = max(nFeatures, max(obj.featureNumbers)) | |
| 299 features = loadTrajectoriesFromSqlite(filename, 'feature', nFeatures+1, timeStep = timeStep) | |
| 300 for obj in objects: | |
| 301 obj.setFeatures(features) | |
| 302 | |
| 303 except sqlite3.OperationalError as error: | |
| 304 printDBError(error) | |
| 305 return objects | |
| 306 | |
| 307 def loadObjectFeatureFrameNumbers(filename, objectNumbers = None): | |
| 308 'Loads the feature frame numbers for each object' | |
| 309 with sqlite3.connect(filename) as connection: | |
| 310 cursor = connection.cursor() | |
| 311 try: | |
| 312 queryStatement = 'SELECT OF.object_id, TL.trajectory_id, TL.length FROM (SELECT trajectory_id, max(frame_number)-min(frame_number) AS length FROM positions GROUP BY trajectory_id) TL, objects_features OF WHERE TL.trajectory_id = OF.trajectory_id' | |
| 313 if objectNumbers is not None: | |
| 314 queryStatement += ' AND object_id '+getObjectCriteria(objectNumbers) | |
| 315 queryStatement += ' ORDER BY OF.object_id, TL.length DESC' | |
| 316 logging.debug(queryStatement) | |
| 317 cursor.execute(queryStatement) | |
| 318 objectFeatureNumbers = {} | |
| 319 for row in cursor: | |
| 320 objId = row[0] | |
| 321 if objId in objectFeatureNumbers: | |
| 322 objectFeatureNumbers[objId].append(row[1]) | |
| 323 else: | |
| 324 objectFeatureNumbers[objId] = [row[1]] | |
| 325 return objectFeatureNumbers | |
| 326 except sqlite3.OperationalError as error: | |
| 327 printDBError(error) | |
| 328 return None | |
| 329 | |
| 330 def addCurvilinearTrajectoriesFromSqlite(filename, objects): | |
| 331 '''Adds curvilinear positions (s_coordinate, y_coordinate, lane) | |
| 332 from a database to an existing MovingObject dict (indexed by each objects's num)''' | |
| 333 with sqlite3.connect(filename) as connection: | |
| 334 cursor = connection.cursor() | |
| 335 | |
| 336 try: | |
| 337 cursor.execute('SELECT * from curvilinear_positions order by trajectory_id, frame_number') | |
| 338 except sqlite3.OperationalError as error: | |
| 339 printDBError(error) | |
| 340 return [] | |
| 341 | |
| 342 missingObjectNumbers = [] | |
| 343 objNum = None | |
| 344 for row in cursor: | |
| 345 if objNum != row[0]: | |
| 346 objNum = row[0] | |
| 347 if objNum in objects: | |
| 348 objects[objNum].curvilinearPositions = moving.CurvilinearTrajectory() | |
| 349 else: | |
| 350 missingObjectNumbers.append(objNum) | |
| 351 if objNum in objects: | |
| 352 objects[objNum].curvilinearPositions.addPositionSYL(row[2],row[3],row[4]) | |
| 353 if len(missingObjectNumbers) > 0: | |
| 354 print('List of missing objects to attach corresponding curvilinear trajectories: {}'.format(missingObjectNumbers)) | |
| 355 | |
| 356 def saveTrajectoriesToTable(connection, objects, trajectoryType, tablePrefix = None): | |
| 357 'Saves trajectories in table tableName' | |
| 358 cursor = connection.cursor() | |
| 359 # Parse feature and/or object structure and commit to DB | |
| 360 if(trajectoryType == 'feature' or trajectoryType == 'object'): | |
| 361 # Extract features from objects | |
| 362 if trajectoryType == 'object': | |
| 363 features = [] | |
| 364 for obj in objects: | |
| 365 if obj.hasFeatures(): | |
| 366 features += obj.getFeatures() | |
| 367 if len(features) == 0: | |
| 368 print('Warning, objects have no features') # todo save centroid trajectories? | |
| 369 elif trajectoryType == 'feature': | |
| 370 features = objects | |
| 371 # Setup feature queries | |
| 372 if tablePrefix is None: | |
| 373 prefix = '' | |
| 374 else: | |
| 375 prefix = tablePrefix+'_' | |
| 376 createTrajectoryTable(cursor, prefix+"positions") | |
| 377 createTrajectoryTable(cursor, prefix+"velocities") | |
| 378 positionQuery = insertTrajectoryQuery(prefix+"positions") | |
| 379 velocityQuery = insertTrajectoryQuery(prefix+"velocities") | |
| 380 # Setup object queries | |
| 381 if trajectoryType == 'object': | |
| 382 createObjectsTable(cursor) | |
| 383 createObjectsFeaturesTable(cursor) | |
| 384 objectQuery = insertObjectQuery() | |
| 385 objectFeatureQuery = insertObjectFeatureQuery() | |
| 386 for feature in features: | |
| 387 num = feature.getNum() | |
| 388 frameNum = feature.getFirstInstant() | |
| 389 for p in feature.getPositions(): | |
| 390 cursor.execute(positionQuery, (num, frameNum, p.x, p.y)) | |
| 391 frameNum += 1 | |
| 392 velocities = feature.getVelocities() | |
| 393 if velocities is not None: | |
| 394 frameNum = feature.getFirstInstant() | |
| 395 for v in velocities[:-1]: | |
| 396 cursor.execute(velocityQuery, (num, frameNum, v.x, v.y)) | |
| 397 frameNum += 1 | |
| 398 if trajectoryType == 'object': | |
| 399 for obj in objects: | |
| 400 if obj.hasFeatures(): | |
| 401 for feature in obj.getFeatures(): | |
| 402 featureNum = feature.getNum() | |
| 403 cursor.execute(objectFeatureQuery, (obj.getNum(), featureNum)) | |
| 404 cursor.execute(objectQuery, (obj.getNum(), obj.getUserType(), 1)) | |
| 405 # Parse curvilinear position structure | |
| 406 elif(trajectoryType == 'curvilinear'): | |
| 407 createCurvilinearTrajectoryTable(cursor) | |
| 408 curvilinearQuery = "INSERT INTO curvilinear_positions VALUES (?,?,?,?,?)" | |
| 409 for obj in objects: | |
| 410 num = obj.getNum() | |
| 411 frameNum = obj.getFirstInstant() | |
| 412 for p in obj.getCurvilinearPositions(): | |
| 413 cursor.execute(curvilinearQuery, (num, frameNum, p[0], p[1], p[2])) | |
| 414 frameNum += 1 | |
| 415 else: | |
| 416 print('Unknown trajectory type {}'.format(trajectoryType)) | |
| 417 connection.commit() | |
| 418 | |
| 419 def saveTrajectoriesToSqlite(outputFilename, objects, trajectoryType): | |
| 420 '''Writes features, ie the trajectory positions (and velocities if exist) | |
| 421 with their instants to a specified sqlite file | |
| 422 Either feature positions (and velocities if they exist) | |
| 423 or curvilinear positions will be saved at a time''' | |
| 424 | |
| 425 with sqlite3.connect(outputFilename) as connection: | |
| 426 try: | |
| 427 saveTrajectoriesToTable(connection, objects, trajectoryType, None) | |
| 428 except sqlite3.OperationalError as error: | |
| 429 printDBError(error) | |
| 430 | |
| 431 def setRoadUserTypes(filename, objects): | |
| 432 '''Saves the user types of the objects in the sqlite database stored in filename | |
| 433 The objects should exist in the objects table''' | |
| 434 with sqlite3.connect(filename) as connection: | |
| 435 cursor = connection.cursor() | |
| 436 for obj in objects: | |
| 437 cursor.execute('update objects set road_user_type = {} WHERE object_id = {}'.format(obj.getUserType(), obj.getNum())) | |
| 438 connection.commit() | |
| 439 | |
| 440 def loadBBMovingObjectsFromSqlite(filename, objectType = 'bb', objectNumbers = None, timeStep = None): | |
| 441 '''Loads bounding box moving object from an SQLite | |
| 442 (format of SQLite output by the ground truth annotation tool | |
| 443 or Urban Tracker | |
| 444 | |
| 445 Load descriptions?''' | |
| 446 objects = [] | |
| 447 with sqlite3.connect(filename) as connection: | |
| 448 if objectType == 'bb': | |
| 449 topCorners = loadTrajectoriesFromTable(connection, 'bounding_boxes', 'bbtop', objectNumbers, timeStep) | |
| 450 bottomCorners = loadTrajectoriesFromTable(connection, 'bounding_boxes', 'bbbottom', objectNumbers, timeStep) | |
| 451 userTypes = loadUserTypesFromTable(connection.cursor(), objectNumbers) # string format is same as object | |
| 452 | |
| 453 for t, b in zip(topCorners, bottomCorners): | |
| 454 num = t.getNum() | |
| 455 if t.getNum() == b.getNum(): | |
| 456 annotation = moving.BBMovingObject(num, t.getTimeInterval(), t, b, userTypes[num]) | |
| 457 objects.append(annotation) | |
| 458 else: | |
| 459 print ('Unknown type of bounding box {}'.format(objectType)) | |
| 460 return objects | |
| 461 | |
| 462 def saveInteraction(cursor, interaction): | |
| 463 roadUserNumbers = list(interaction.getRoadUserNumbers()) | |
| 464 cursor.execute('INSERT INTO interactions VALUES({}, {}, {}, {}, {})'.format(interaction.getNum(), roadUserNumbers[0], roadUserNumbers[1], interaction.getFirstInstant(), interaction.getLastInstant())) | |
| 465 | |
| 466 def saveInteractionsToSqlite(filename, interactions): | |
| 467 'Saves the interactions in the table' | |
| 468 with sqlite3.connect(filename) as connection: | |
| 469 cursor = connection.cursor() | |
| 470 try: | |
| 471 createInteractionTable(cursor) | |
| 472 for inter in interactions: | |
| 473 saveInteraction(cursor, inter) | |
| 474 except sqlite3.OperationalError as error: | |
| 475 printDBError(error) | |
| 476 connection.commit() | |
| 477 | |
| 478 def saveIndicator(cursor, interactionNum, indicator): | |
| 479 for instant in indicator.getTimeInterval(): | |
| 480 if indicator[instant]: | |
| 481 cursor.execute('INSERT INTO indicators VALUES({}, {}, {}, {})'.format(interactionNum, events.Interaction.indicatorNameToIndices[indicator.getName()], instant, indicator[instant])) | |
| 482 | |
| 483 def saveIndicatorsToSqlite(filename, interactions, indicatorNames = events.Interaction.indicatorNames): | |
| 484 'Saves the indicator values in the table' | |
| 485 with sqlite3.connect(filename) as connection: | |
| 486 cursor = connection.cursor() | |
| 487 try: | |
| 488 createInteractionTable(cursor) | |
| 489 createIndicatorTable(cursor) | |
| 490 for inter in interactions: | |
| 491 saveInteraction(cursor, inter) | |
| 492 for indicatorName in indicatorNames: | |
| 493 indicator = inter.getIndicator(indicatorName) | |
| 494 if indicator is not None: | |
| 495 saveIndicator(cursor, inter.getNum(), indicator) | |
| 496 except sqlite3.OperationalError as error: | |
| 497 printDBError(error) | |
| 498 connection.commit() | |
| 499 | |
| 500 def loadInteractionsFromSqlite(filename): | |
| 501 '''Loads interaction and their indicators | |
| 502 | |
| 503 TODO choose the interactions to load''' | |
| 504 interactions = [] | |
| 505 with sqlite3.connect(filename) as connection: | |
| 506 cursor = connection.cursor() | |
| 507 try: | |
| 508 cursor.execute('SELECT INT.id, INT.object_id1, INT.object_id2, INT.first_frame_number, INT.last_frame_number, IND.indicator_type, IND.frame_number, IND.value from interactions INT, indicators IND WHERE INT.id = IND.interaction_id ORDER BY INT.id, IND.indicator_type, IND.frame_number') | |
| 509 interactionNum = -1 | |
| 510 indicatorTypeNum = -1 | |
| 511 tmpIndicators = {} | |
| 512 for row in cursor: | |
| 513 if row[0] != interactionNum: | |
| 514 interactionNum = row[0] | |
| 515 interactions.append(events.Interaction(interactionNum, moving.TimeInterval(row[3],row[4]), row[1], row[2])) | |
| 516 interactions[-1].indicators = {} | |
| 517 if indicatorTypeNum != row[5] or row[0] != interactionNum: | |
| 518 indicatorTypeNum = row[5] | |
| 519 indicatorName = events.Interaction.indicatorNames[indicatorTypeNum] | |
| 520 indicatorValues = {row[6]:row[7]} | |
| 521 interactions[-1].indicators[indicatorName] = indicators.SeverityIndicator(indicatorName, indicatorValues, mostSevereIsMax = not indicatorName in events.Interaction.timeIndicators) | |
| 522 else: | |
| 523 indicatorValues[row[6]] = row[7] | |
| 524 interactions[-1].indicators[indicatorName].timeInterval.last = row[6] | |
| 525 except sqlite3.OperationalError as error: | |
| 526 printDBError(error) | |
| 527 return [] | |
| 528 return interactions | |
| 529 # load first and last object instants | |
| 530 # CREATE TEMP TABLE IF NOT EXISTS object_instants AS SELECT OF.object_id, min(frame_number) as first_instant, max(frame_number) as last_instant from positions P, objects_features OF WHERE P.trajectory_id = OF.trajectory_id group by OF.object_id order by OF.object_id | |
| 531 | |
| 532 def createBoundingBoxTable(filename, invHomography = None): | |
| 533 '''Create the table to store the object bounding boxes in image space | |
| 534 ''' | |
| 535 with sqlite3.connect(filename) as connection: | |
| 536 cursor = connection.cursor() | |
| 537 try: | |
| 538 cursor.execute('CREATE TABLE IF NOT EXISTS bounding_boxes (object_id INTEGER, frame_number INTEGER, x_top_left REAL, y_top_left REAL, x_bottom_right REAL, y_bottom_right REAL, PRIMARY KEY(object_id, frame_number))') | |
| 539 cursor.execute('INSERT INTO bounding_boxes SELECT object_id, frame_number, min(x), min(y), max(x), max(y) from ' | |
| 540 '(SELECT object_id, frame_number, (x*{}+y*{}+{})/w as x, (x*{}+y*{}+{})/w as y from ' | |
| 541 '(SELECT OF.object_id, P.frame_number, P.x_coordinate as x, P.y_coordinate as y, P.x_coordinate*{}+P.y_coordinate*{}+{} as w from positions P, objects_features OF WHERE P.trajectory_id = OF.trajectory_id)) '.format(invHomography[0,0], invHomography[0,1], invHomography[0,2], invHomography[1,0], invHomography[1,1], invHomography[1,2], invHomography[2,0], invHomography[2,1], invHomography[2,2])+ | |
| 542 'GROUP BY object_id, frame_number') | |
| 543 except sqlite3.OperationalError as error: | |
| 544 printDBError(error) | |
| 545 connection.commit() | |
| 546 | |
| 547 def loadBoundingBoxTableForDisplay(filename): | |
| 548 '''Loads bounding boxes from bounding_boxes table for display over trajectories''' | |
| 549 boundingBoxes = {} # list of bounding boxes for each instant | |
| 550 with sqlite3.connect(filename) as connection: | |
| 551 cursor = connection.cursor() | |
| 552 try: | |
| 553 cursor.execute('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'bounding_boxes\'') | |
| 554 result = cursor.fetchall() | |
| 555 if len(result) > 0: | |
| 556 cursor.execute('SELECT * FROM bounding_boxes') | |
| 557 for row in cursor: | |
| 558 boundingBoxes.setdefault(row[1], []).append([moving.Point(row[2], row[3]), moving.Point(row[4], row[5])]) | |
| 559 except sqlite3.OperationalError as error: | |
| 560 printDBError(error) | |
| 561 return boundingBoxes | |
| 562 return boundingBoxes | |
| 563 | |
| 564 ######################### | |
| 565 # saving and loading for scene interpretation: POIs and Prototypes | |
| 566 ######################### | |
| 567 | |
| 568 def savePrototypesToSqlite(filename, prototypes): | |
| 569 '''save the prototypes (a prototype is defined by a filename, a number (id) and type''' | |
| 570 with sqlite3.connect(filename) as connection: | |
| 571 cursor = connection.cursor() | |
| 572 try: | |
| 573 cursor.execute('CREATE TABLE IF NOT EXISTS prototypes (prototype_filename VARCHAR, prototype_id INTEGER, trajectory_type VARCHAR CHECK (trajectory_type IN (\"feature\", \"object\")), nmatchings INTEGER, PRIMARY KEY (prototype_filename, prototype_id, trajectory_type))') | |
| 574 for p in prototypes: | |
| 575 cursor.execute('INSERT INTO prototypes VALUES(?,?,?,?)', (p.getFilename(), p.getNum(), p.getTrajectoryType(), p.getNMatchings())) | |
| 576 except sqlite3.OperationalError as error: | |
| 577 printDBError(error) | |
| 578 connection.commit() | |
| 579 | |
| 580 def savePrototypeAssignmentsToSqlite(filename, objects, objectType, labels, prototypes): | |
| 581 with sqlite3.connect(filename) as connection: | |
| 582 cursor = connection.cursor() | |
| 583 try: | |
| 584 if objectType == 'feature': | |
| 585 tableName = 'features_prototypes' | |
| 586 objectIdColumnName = 'trajectory_id' | |
| 587 elif objectType == 'object': | |
| 588 tableName = 'objects_prototypes' | |
| 589 objectIdColumnName = 'object_id' | |
| 590 cursor.execute('CREATE TABLE IF NOT EXISTS '+tableName+' ('+objectIdColumnName+' INTEGER, prototype_filename VARCHAR, prototype_id INTEGER, trajectory_type VARCHAR CHECK (trajectory_type IN (\"feature\", \"object\")), PRIMARY KEY('+objectIdColumnName+', prototype_filename, prototype_id, trajectory_type))') | |
| 591 for obj, label in zip(objects, labels): | |
| 592 proto = prototypes[label] | |
| 593 cursor.execute('INSERT INTO objects_prototypes VALUES(?,?,?,?)', (obj.getNum(), proto.getFilename(), proto.getNum(), proto.getTrajectoryType())) | |
| 594 except sqlite3.OperationalError as error: | |
| 595 printDBError(error) | |
| 596 connection.commit() | |
| 597 | |
| 598 def loadPrototypesFromSqlite(filename, withTrajectories = True): | |
| 599 'Loads prototype ids and matchings (if stored)' | |
| 600 prototypes = [] | |
| 601 with sqlite3.connect(filename) as connection: | |
| 602 cursor = connection.cursor() | |
| 603 objects = [] | |
| 604 try: | |
| 605 cursor.execute('SELECT * FROM prototypes') | |
| 606 for row in cursor: | |
| 607 prototypes.append(moving.Prototype(row[0], row[1], row[2], row[3])) | |
| 608 if withTrajectories: | |
| 609 for p in prototypes: | |
| 610 p.setMovingObject(loadTrajectoriesFromSqlite(p.getFilename(), p.getTrajectoryType(), [p.getNum()])[0]) | |
| 611 # loadingInformation = {} # complicated slightly optimized | |
| 612 # for p in prototypes: | |
| 613 # dbfn = p.getFilename() | |
| 614 # trajType = p.getTrajectoryType() | |
| 615 # if (dbfn, trajType) in loadingInformation: | |
| 616 # loadingInformation[(dbfn, trajType)].append(p) | |
| 617 # else: | |
| 618 # loadingInformation[(dbfn, trajType)] = [p] | |
| 619 # for k, v in loadingInformation.iteritems(): | |
| 620 # objects += loadTrajectoriesFromSqlite(k[0], k[1], [p.getNum() for p in v]) | |
| 621 except sqlite3.OperationalError as error: | |
| 622 printDBError(error) | |
| 623 if len(set([p.getTrajectoryType() for p in prototypes])) > 1: | |
| 624 print('Different types of prototypes in database ({}).'.format(set([p.getTrajectoryType() for p in prototypes]))) | |
| 625 return prototypes | |
| 626 | |
| 627 def savePOIsToSqlite(filename, gmm, gmmType, gmmId): | |
| 628 '''Saves a Gaussian mixture model (of class sklearn.mixture.GaussianMixture) | |
| 629 gmmType is a type of GaussianMixture, learnt either from beginnings or ends of trajectories''' | |
| 630 with sqlite3.connect(filename) as connection: | |
| 631 cursor = connection.cursor() | |
| 632 if gmmType not in ['beginning', 'end']: | |
| 633 print('Unknown POI type {}. Exiting'.format(gmmType)) | |
| 634 import sys | |
| 635 sys.exit() | |
| 636 try: | |
| 637 cursor.execute('CREATE TABLE IF NOT EXISTS gaussians2d (poi_id INTEGER, id INTEGER, type VARCHAR, x_center REAL, y_center REAL, covariance VARCHAR, covariance_type VARCHAR, weight, precisions_cholesky VARCHAR, PRIMARY KEY(poi_id, id))') | |
| 638 for i in range(gmm.n_components): | |
| 639 cursor.execute('INSERT INTO gaussians2d VALUES(?,?,?,?,?,?,?,?,?)', (gmmId, i, gmmType, gmm.means_[i][0], gmm.means_[i][1], str(gmm.covariances_[i].tolist()), gmm.covariance_type, gmm.weights_[i], str(gmm.precisions_cholesky_[i].tolist()))) | |
| 640 connection.commit() | |
| 641 except sqlite3.OperationalError as error: | |
| 642 printDBError(error) | |
| 643 | |
| 644 def savePOIAssignmentsToSqlite(filename, objects): | |
| 645 'save the od fields of objects' | |
| 646 with sqlite3.connect(filename) as connection: | |
| 647 cursor = connection.cursor() | |
| 648 try: | |
| 649 cursor.execute('CREATE TABLE IF NOT EXISTS objects_pois (object_id INTEGER, origin_poi_id INTEGER, destination_poi_id INTEGER, PRIMARY KEY(object_id))') | |
| 650 for o in objects: | |
| 651 cursor.execute('INSERT INTO objects_pois VALUES(?,?,?)', (o.getNum(), o.od[0], o.od[1])) | |
| 652 connection.commit() | |
| 653 except sqlite3.OperationalError as error: | |
| 654 printDBError(error) | |
| 655 | |
| 656 def loadPOIsFromSqlite(filename): | |
| 657 'Loads all 2D Gaussians in the database' | |
| 658 from sklearn import mixture # todo if not avalaible, load data in duck-typed class with same fields | |
| 659 from ast import literal_eval | |
| 660 pois = [] | |
| 661 with sqlite3.connect(filename) as connection: | |
| 662 cursor = connection.cursor() | |
| 663 try: | |
| 664 cursor.execute('SELECT * from gaussians2d') | |
| 665 gmmId = None | |
| 666 gmm = [] | |
| 667 for row in cursor: | |
| 668 if gmmId is None or row[0] != gmmId: | |
| 669 if len(gmm) > 0: | |
| 670 tmp = mixture.GaussianMixture(len(gmm), covarianceType) | |
| 671 tmp.means_ = array([gaussian['mean'] for gaussian in gmm]) | |
| 672 tmp.covariances_ = array([gaussian['covar'] for gaussian in gmm]) | |
| 673 tmp.weights_ = array([gaussian['weight'] for gaussian in gmm]) | |
| 674 tmp.gmmTypes = [gaussian['type'] for gaussian in gmm] | |
| 675 tmp.precisions_cholesky_ = array([gaussian['precisions'] for gaussian in gmm]) | |
| 676 pois.append(tmp) | |
| 677 gaussian = {'type': row[2], | |
| 678 'mean': row[3:5], | |
| 679 'covar': array(literal_eval(row[5])), | |
| 680 'weight': row[7], | |
| 681 'precisions': array(literal_eval(row[8]))} | |
| 682 gmm = [gaussian] | |
| 683 covarianceType = row[6] | |
| 684 gmmId = row[0] | |
| 685 else: | |
| 686 gmm.append({'type': row[2], | |
| 687 'mean': row[3:5], | |
| 688 'covar': array(literal_eval(row[5])), | |
| 689 'weight': row[7], | |
| 690 'precisions': array(literal_eval(row[8]))}) | |
| 691 if len(gmm) > 0: | |
| 692 tmp = mixture.GaussianMixture(len(gmm), covarianceType) | |
| 693 tmp.means_ = array([gaussian['mean'] for gaussian in gmm]) | |
| 694 tmp.covariances_ = array([gaussian['covar'] for gaussian in gmm]) | |
| 695 tmp.weights_ = array([gaussian['weight'] for gaussian in gmm]) | |
| 696 tmp.gmmTypes = [gaussian['type'] for gaussian in gmm] | |
| 697 tmp.precisions_cholesky_ = array([gaussian['precisions'] for gaussian in gmm]) | |
| 698 pois.append(tmp) | |
| 699 except sqlite3.OperationalError as error: | |
| 700 printDBError(error) | |
| 701 return pois | |
| 702 | |
| 703 ######################### | |
| 704 # saving and loading for scene interpretation (Mohamed Gomaa Mohamed's PhD) | |
| 705 ######################### | |
| 706 | |
| 707 def writePrototypesToSqlite(prototypes,nMatching, outputFilename): | |
| 708 ''' prototype dataset is a dictionary with keys== routes, values== prototypes Ids ''' | |
| 709 connection = sqlite3.connect(outputFilename) | |
| 710 cursor = connection.cursor() | |
| 711 | |
| 712 cursor.execute('CREATE TABLE IF NOT EXISTS prototypes (prototype_id INTEGER,routeIDstart INTEGER,routeIDend INTEGER, nMatching INTEGER, PRIMARY KEY(prototype_id))') | |
| 713 | |
| 714 for route in prototypes: | |
| 715 if prototypes[route]!=[]: | |
| 716 for i in prototypes[route]: | |
| 717 cursor.execute('insert into prototypes (prototype_id, routeIDstart,routeIDend, nMatching) values (?,?,?,?)',(i,route[0],route[1],nMatching[route][i])) | |
| 718 | |
| 719 connection.commit() | |
| 720 connection.close() | |
| 721 | |
| 722 def readPrototypesFromSqlite(filename): | |
| 723 ''' | |
| 724 This function loads the prototype file in the database | |
| 725 It returns a dictionary for prototypes for each route and nMatching | |
| 726 ''' | |
| 727 prototypes = {} | |
| 728 nMatching={} | |
| 729 | |
| 730 connection = sqlite3.connect(filename) | |
| 731 cursor = connection.cursor() | |
| 732 | |
| 733 try: | |
| 734 cursor.execute('SELECT * from prototypes order by prototype_id, routeIDstart,routeIDend, nMatching') | |
| 735 except sqlite3.OperationalError as error: | |
| 736 printDBError(error) | |
| 737 return [] | |
| 738 | |
| 739 for row in cursor: | |
| 740 route=(row[1],row[2]) | |
| 741 if route not in prototypes: | |
| 742 prototypes[route]=[] | |
| 743 prototypes[route].append(row[0]) | |
| 744 nMatching[row[0]]=row[3] | |
| 745 | |
| 746 connection.close() | |
| 747 return prototypes,nMatching | |
| 748 | |
| 749 def writeLabelsToSqlite(labels, outputFilename): | |
| 750 """ labels is a dictionary with keys: routes, values: prototypes Ids | |
| 751 """ | |
| 752 connection = sqlite3.connect(outputFilename) | |
| 753 cursor = connection.cursor() | |
| 754 | |
| 755 cursor.execute("CREATE TABLE IF NOT EXISTS labels (object_id INTEGER,routeIDstart INTEGER,routeIDend INTEGER, prototype_id INTEGER, PRIMARY KEY(object_id))") | |
| 756 | |
| 757 for route in labels: | |
| 758 if labels[route]!=[]: | |
| 759 for i in labels[route]: | |
| 760 for j in labels[route][i]: | |
| 761 cursor.execute("insert into labels (object_id, routeIDstart,routeIDend, prototype_id) values (?,?,?,?)",(j,route[0],route[1],i)) | |
| 762 | |
| 763 connection.commit() | |
| 764 connection.close() | |
| 765 | |
| 766 def loadLabelsFromSqlite(filename): | |
| 767 labels = {} | |
| 768 | |
| 769 connection = sqlite3.connect(filename) | |
| 770 cursor = connection.cursor() | |
| 771 | |
| 772 try: | |
| 773 cursor.execute('SELECT * from labels order by object_id, routeIDstart,routeIDend, prototype_id') | |
| 774 except sqlite3.OperationalError as error: | |
| 775 printDBError(error) | |
| 776 return [] | |
| 777 | |
| 778 for row in cursor: | |
| 779 route=(row[1],row[2]) | |
| 780 p=row[3] | |
| 781 if route not in labels: | |
| 782 labels[route]={} | |
| 783 if p not in labels[route]: | |
| 784 labels[route][p]=[] | |
| 785 labels[route][p].append(row[0]) | |
| 786 | |
| 787 connection.close() | |
| 788 return labels | |
| 789 | |
| 790 def writeSpeedPrototypeToSqlite(prototypes,nmatching, outFilename): | |
| 791 """ to match the format of second layer prototypes""" | |
| 792 connection = sqlite3.connect(outFilename) | |
| 793 cursor = connection.cursor() | |
| 794 | |
| 795 cursor.execute("CREATE TABLE IF NOT EXISTS speedprototypes (spdprototype_id INTEGER,prototype_id INTEGER,routeID_start INTEGER, routeID_end INTEGER, nMatching INTEGER, PRIMARY KEY(spdprototype_id))") | |
| 796 | |
| 797 for route in prototypes: | |
| 798 if prototypes[route]!={}: | |
| 799 for i in prototypes[route]: | |
| 800 if prototypes[route][i]!= []: | |
| 801 for j in prototypes[route][i]: | |
| 802 cursor.execute("insert into speedprototypes (spdprototype_id,prototype_id, routeID_start, routeID_end, nMatching) values (?,?,?,?,?)",(j,i,route[0],route[1],nmatching[j])) | |
| 803 | |
| 804 connection.commit() | |
| 805 connection.close() | |
| 806 | |
| 807 def loadSpeedPrototypeFromSqlite(filename): | |
| 808 """ | |
| 809 This function loads the prototypes table in the database of name <filename>. | |
| 810 """ | |
| 811 prototypes = {} | |
| 812 nMatching={} | |
| 813 connection = sqlite3.connect(filename) | |
| 814 cursor = connection.cursor() | |
| 815 | |
| 816 try: | |
| 817 cursor.execute('SELECT * from speedprototypes order by spdprototype_id,prototype_id, routeID_start, routeID_end, nMatching') | |
| 818 except sqlite3.OperationalError as error: | |
| 819 printDBError(error) | |
| 820 return [] | |
| 821 | |
| 822 for row in cursor: | |
| 823 route=(row[2],row[3]) | |
| 824 if route not in prototypes: | |
| 825 prototypes[route]={} | |
| 826 if row[1] not in prototypes[route]: | |
| 827 prototypes[route][row[1]]=[] | |
| 828 prototypes[route][row[1]].append(row[0]) | |
| 829 nMatching[row[0]]=row[4] | |
| 830 | |
| 831 connection.close() | |
| 832 return prototypes,nMatching | |
| 833 | |
| 834 | |
| 835 def writeRoutesToSqlite(Routes, outputFilename): | |
| 836 """ This function writes the activity path define by start and end IDs""" | |
| 837 connection = sqlite3.connect(outputFilename) | |
| 838 cursor = connection.cursor() | |
| 839 | |
| 840 cursor.execute("CREATE TABLE IF NOT EXISTS routes (object_id INTEGER,routeIDstart INTEGER,routeIDend INTEGER, PRIMARY KEY(object_id))") | |
| 841 | |
| 842 for route in Routes: | |
| 843 if Routes[route]!=[]: | |
| 844 for i in Routes[route]: | |
| 845 cursor.execute("insert into routes (object_id, routeIDstart,routeIDend) values (?,?,?)",(i,route[0],route[1])) | |
| 846 | |
| 847 connection.commit() | |
| 848 connection.close() | |
| 849 | |
| 850 def loadRoutesFromSqlite(filename): | |
| 851 Routes = {} | |
| 852 | |
| 853 connection = sqlite3.connect(filename) | |
| 854 cursor = connection.cursor() | |
| 855 | |
| 856 try: | |
| 857 cursor.execute('SELECT * from routes order by object_id, routeIDstart,routeIDend') | |
| 858 except sqlite3.OperationalError as error: | |
| 859 printDBError(error) | |
| 860 return [] | |
| 861 | |
| 862 for row in cursor: | |
| 863 route=(row[1],row[2]) | |
| 864 if route not in Routes: | |
| 865 Routes[route]=[] | |
| 866 Routes[route].append(row[0]) | |
| 867 | |
| 868 connection.close() | |
| 869 return Routes | |
| 870 | |
| 871 def setRoutes(filename, objects): | |
| 872 connection = sqlite3.connect(filename) | |
| 873 cursor = connection.cursor() | |
| 874 for obj in objects: | |
| 875 cursor.execute('update objects set startRouteID = {} WHERE object_id = {}'.format(obj.startRouteID, obj.getNum())) | |
| 876 cursor.execute('update objects set endRouteID = {} WHERE object_id = {}'.format(obj.endRouteID, obj.getNum())) | |
| 877 connection.commit() | |
| 878 connection.close() | |
| 879 | |
| 880 ######################### | |
| 881 # txt files | |
| 882 ######################### | |
| 883 | |
| 884 def openCheck(filename, option = 'r', quitting = False): | |
| 885 '''Open file filename in read mode by default | |
| 886 and checks it is open''' | |
| 887 try: | |
| 888 return open(filename, option) | |
| 889 except IOError: | |
| 890 print('File {} could not be opened.'.format(filename)) | |
| 891 if quitting: | |
| 892 from sys import exit | |
| 893 exit() | |
| 894 return None | |
| 895 | |
| 896 def readline(f, commentCharacters = commentChar): | |
| 897 '''Modified readline function to skip comments | |
| 898 Can take a list of characters or a string (in will work in both)''' | |
| 899 s = f.readline() | |
| 900 while (len(s) > 0) and s[0] in commentCharacters: | |
| 901 s = f.readline() | |
| 902 return s.strip() | |
| 903 | |
| 904 def getLines(f, delimiterChar = delimiterChar, commentCharacters = commentChar): | |
| 905 '''Gets a complete entry (all the lines) in between delimiterChar.''' | |
| 906 dataStrings = [] | |
| 907 s = readline(f, commentCharacters) | |
| 908 while len(s) > 0 and s[0] != delimiterChar: | |
| 909 dataStrings += [s.strip()] | |
| 910 s = readline(f, commentCharacters) | |
| 911 return dataStrings | |
| 912 | |
| 913 def saveList(filename, l): | |
| 914 f = openCheck(filename, 'w') | |
| 915 for x in l: | |
| 916 f.write('{}\n'.format(x)) | |
| 917 f.close() | |
| 918 | |
| 919 def loadListStrings(filename, commentCharacters = commentChar): | |
| 920 f = openCheck(filename, 'r') | |
| 921 result = getLines(f, commentCharacters) | |
| 922 f.close() | |
| 923 return result | |
| 924 | |
| 925 def getValuesFromINIFile(filename, option, delimiterChar = '=', commentCharacters = commentChar): | |
| 926 values = [] | |
| 927 for l in loadListStrings(filename, commentCharacters): | |
| 928 if l.startswith(option): | |
| 929 values.append(l.split(delimiterChar)[1].strip()) | |
| 930 return values | |
| 931 | |
| 932 def addSectionHeader(propertiesFile, headerName = 'main'): | |
| 933 '''Add fake section header | |
| 934 | |
| 935 from http://stackoverflow.com/questions/2819696/parsing-properties-file-in-python/2819788#2819788 | |
| 936 use read_file in Python 3.2+ | |
| 937 ''' | |
| 938 yield '[{}]\n'.format(headerName) | |
| 939 for line in propertiesFile: | |
| 940 yield line | |
| 941 | |
| 942 def loadPemsTraffic(filename): | |
| 943 '''Loads traffic data downloaded from the http://pems.dot.ca.gov clearinghouse | |
| 944 into pandas dataframe''' | |
| 945 f = openCheck(filename) | |
| 946 l = f.readline().strip() | |
| 947 items = l.split(',') | |
| 948 headers = ['time', 'station', 'district', 'route', 'direction', 'lanetype', 'length', 'nsamples', 'pctobserved', 'flow', 'occupancy', 'speed', 'delay35', 'delay40', 'delay45', 'delay50', 'delay55', 'delay60'] | |
| 949 nLanes = (len(items)-len(headers))/3 | |
| 950 for i in range(nLanes): | |
| 951 headers += ['flow{}'.format(i+1), 'occupancy{}'.format(i+1), 'speed{}'.format(i+1)] | |
| 952 f.close() | |
| 953 return read_csv(filename, delimiter = ',', names = headers) | |
| 954 | |
| 955 def generatePDLaneColumn(data): | |
| 956 data['LANE'] = data['LANE\\LINK\\NO'].astype(str)+'_'+data['LANE\\INDEX'].astype(str) | |
| 957 | |
| 958 def convertTrajectoriesVissimToSqlite(filename): | |
| 959 '''Relies on a system call to sqlite3 | |
| 960 sqlite3 [file.sqlite] < import_fzp.sql''' | |
| 961 sqlScriptFilename = "import_fzp.sql" | |
| 962 # create sql file | |
| 963 out = openCheck(sqlScriptFilename, "w") | |
| 964 out.write(".separator \";\"\n"+ | |
| 965 "CREATE TABLE IF NOT EXISTS curvilinear_positions (t REAL, trajectory_id INTEGER, link_id INTEGER, lane_id INTEGER, s_coordinate REAL, y_coordinate REAL, speed REAL, PRIMARY KEY (t, trajectory_id));\n"+ | |
| 966 ".import "+filename+" curvilinear_positions\n"+ | |
| 967 "DELETE FROM curvilinear_positions WHERE trajectory_id IS NULL OR trajectory_id = \"NO\";\n") | |
| 968 out.close() | |
| 969 # system call | |
| 970 from subprocess import run | |
| 971 out = openCheck("err.log", "w") | |
| 972 run("sqlite3 "+utils.removeExtension(filename)+".sqlite < "+sqlScriptFilename, stderr = out) | |
| 973 out.close() | |
| 974 shutil.os.remove(sqlScriptFilename) | |
| 975 | |
| 976 def loadObjectNumbersInLinkFromVissimFile(filename, linkIds): | |
| 977 '''Finds the ids of the objects that go through any of the link in the list linkIds''' | |
| 978 with sqlite3.connect(filename) as connection: | |
| 979 cursor = connection.cursor() | |
| 980 queryStatement = 'SELECT DISTINCT trajectory_id FROM curvilinear_positions where link_id IN ('+','.join([str(id) for id in linkIds])+')' | |
| 981 try: | |
| 982 cursor.execute(queryStatement) | |
| 983 return [row[0] for row in cursor] | |
| 984 except sqlite3.OperationalError as error: | |
| 985 printDBError(error) | |
| 986 | |
| 987 def getNObjectsInLinkFromVissimFile(filename, linkIds): | |
| 988 '''Returns the number of objects that traveled through the link ids''' | |
| 989 with sqlite3.connect(filename) as connection: | |
| 990 cursor = connection.cursor() | |
| 991 queryStatement = 'SELECT link_id, COUNT(DISTINCT trajectory_id) FROM curvilinear_positions where link_id IN ('+','.join([str(id) for id in linkIds])+') GROUP BY link_id' | |
| 992 try: | |
| 993 cursor.execute(queryStatement) | |
| 994 return {row[0]:row[1] for row in cursor} | |
| 995 except sqlite3.OperationalError as error: | |
| 996 printDBError(error) | |
| 997 | |
| 998 def loadTrajectoriesFromVissimFile(filename, simulationStepsPerTimeUnit, objectNumbers = None, warmUpLastInstant = None, usePandas = False, nDecimals = 2, lowMemory = True): | |
| 999 '''Reads data from VISSIM .fzp trajectory file | |
| 1000 simulationStepsPerTimeUnit is the number of simulation steps per unit of time used by VISSIM (second) | |
| 1001 for example, there seems to be 10 simulation steps per simulated second in VISSIM, | |
| 1002 so simulationStepsPerTimeUnit should be 10, | |
| 1003 so that all times correspond to the number of the simulation step (and can be stored as integers) | |
| 1004 | |
| 1005 Objects positions will be considered only after warmUpLastInstant | |
| 1006 (if the object has no such position, it won't be loaded) | |
| 1007 | |
| 1008 Assumed to be sorted over time | |
| 1009 Warning: if reading from SQLite a limited number of objects, objectNumbers will be the maximum object id''' | |
| 1010 objects = {} # dictionary of objects index by their id | |
| 1011 | |
| 1012 if usePandas: | |
| 1013 data = read_csv(filename, delimiter=';', comment='*', header=0, skiprows = 1, low_memory = lowMemory) | |
| 1014 generatePDLaneColumn(data) | |
| 1015 data['TIME'] = data['$VEHICLE:SIMSEC']*simulationStepsPerTimeUnit | |
| 1016 if warmUpLastInstant is not None: | |
| 1017 data = data[data['TIME']>=warmUpLastInstant] | |
| 1018 grouped = data.loc[:,['NO','TIME']].groupby(['NO'], as_index = False) | |
| 1019 instants = grouped['TIME'].agg({'first': npmin, 'last': npmax}) | |
| 1020 for row_index, row in instants.iterrows(): | |
| 1021 objNum = int(row['NO']) | |
| 1022 tmp = data[data['NO'] == objNum] | |
| 1023 objects[objNum] = moving.MovingObject(num = objNum, timeInterval = moving.TimeInterval(row['first'], row['last'])) | |
| 1024 # positions should be rounded to nDecimals decimals only | |
| 1025 objects[objNum].curvilinearPositions = moving.CurvilinearTrajectory(S = npround(tmp['POS'].tolist(), nDecimals), Y = npround(tmp['POSLAT'].tolist(), nDecimals), lanes = tmp['LANE'].tolist()) | |
| 1026 if objectNumbers is not None and objectNumbers > 0 and len(objects) >= objectNumbers: | |
| 1027 return list(objects.values()) | |
| 1028 else: | |
| 1029 if filename.endswith(".fzp"): | |
| 1030 inputfile = openCheck(filename, quitting = True) | |
| 1031 line = readline(inputfile, '*$') | |
| 1032 while len(line) > 0:#for line in inputfile: | |
| 1033 data = line.strip().split(';') | |
| 1034 objNum = int(data[1]) | |
| 1035 instant = float(data[0])*simulationStepsPerTimeUnit | |
| 1036 s = float(data[4]) | |
| 1037 y = float(data[5]) | |
| 1038 lane = data[2]+'_'+data[3] | |
| 1039 if objNum not in objects: | |
| 1040 if warmUpLastInstant is None or instant >= warmUpLastInstant: | |
| 1041 if objectNumbers is None or len(objects) < objectNumbers: | |
| 1042 objects[objNum] = moving.MovingObject(num = objNum, timeInterval = moving.TimeInterval(instant, instant)) | |
| 1043 objects[objNum].curvilinearPositions = moving.CurvilinearTrajectory() | |
| 1044 if (warmUpLastInstant is None or instant >= warmUpLastInstant) and objNum in objects: | |
| 1045 objects[objNum].timeInterval.last = instant | |
| 1046 objects[objNum].curvilinearPositions.addPositionSYL(s, y, lane) | |
| 1047 line = readline(inputfile, '*$') | |
| 1048 elif filename.endswith(".sqlite"): | |
| 1049 with sqlite3.connect(filename) as connection: | |
| 1050 cursor = connection.cursor() | |
| 1051 queryStatement = 'SELECT t, trajectory_id, link_id, lane_id, s_coordinate, y_coordinate FROM curvilinear_positions' | |
| 1052 if objectNumbers is not None: | |
| 1053 queryStatement += ' WHERE trajectory_id '+getObjectCriteria(objectNumbers) | |
| 1054 queryStatement += ' ORDER BY trajectory_id, t' | |
| 1055 try: | |
| 1056 cursor.execute(queryStatement) | |
| 1057 for row in cursor: | |
| 1058 objNum = row[1] | |
| 1059 instant = row[0]*simulationStepsPerTimeUnit | |
| 1060 s = row[4] | |
| 1061 y = row[5] | |
| 1062 lane = '{}_{}'.format(row[2], row[3]) | |
| 1063 if objNum not in objects: | |
| 1064 if warmUpLastInstant is None or instant >= warmUpLastInstant: | |
| 1065 if objectNumbers is None or len(objects) < objectNumbers: | |
| 1066 objects[objNum] = moving.MovingObject(num = objNum, timeInterval = moving.TimeInterval(instant, instant)) | |
| 1067 objects[objNum].curvilinearPositions = moving.CurvilinearTrajectory() | |
| 1068 if (warmUpLastInstant is None or instant >= warmUpLastInstant) and objNum in objects: | |
| 1069 objects[objNum].timeInterval.last = instant | |
| 1070 objects[objNum].curvilinearPositions.addPositionSYL(s, y, lane) | |
| 1071 except sqlite3.OperationalError as error: | |
| 1072 printDBError(error) | |
| 1073 else: | |
| 1074 print("File type of "+filename+" not supported (only .sqlite and .fzp files)") | |
| 1075 return list(objects.values()) | |
| 1076 | |
| 1077 def selectPDLanes(data, lanes = None): | |
| 1078 '''Selects the subset of data for the right lanes | |
| 1079 | |
| 1080 Lane format is a string 'x_y' where x is link index and y is lane index''' | |
| 1081 if lanes is not None: | |
| 1082 if 'LANE' not in data.columns: | |
| 1083 generatePDLaneColumn(data) | |
| 1084 indices = (data['LANE'] == lanes[0]) | |
| 1085 for l in lanes[1:]: | |
| 1086 indices = indices | (data['LANE'] == l) | |
| 1087 return data[indices] | |
| 1088 else: | |
| 1089 return data | |
| 1090 | |
| 1091 def countStoppedVehiclesVissim(filename, lanes = None, proportionStationaryTime = 0.7): | |
| 1092 '''Counts the number of vehicles stopped for a long time in a VISSIM trajectory file | |
| 1093 and the total number of vehicles | |
| 1094 | |
| 1095 Vehicles are considered finally stationary | |
| 1096 if more than proportionStationaryTime of their total time | |
| 1097 If lanes is not None, only the data for the selected lanes will be provided | |
| 1098 (format as string x_y where x is link index and y is lane index)''' | |
| 1099 if filename.endswith(".fzp"): | |
| 1100 columns = ['NO', '$VEHICLE:SIMSEC', 'POS'] | |
| 1101 if lanes is not None: | |
| 1102 columns += ['LANE\\LINK\\NO', 'LANE\\INDEX'] | |
| 1103 data = read_csv(filename, delimiter=';', comment='*', header=0, skiprows = 1, usecols = columns, low_memory = lowMemory) | |
| 1104 data = selectPDLanes(data, lanes) | |
| 1105 data.sort(['$VEHICLE:SIMSEC'], inplace = True) | |
| 1106 | |
| 1107 nStationary = 0 | |
| 1108 nVehicles = 0 | |
| 1109 for name, group in data.groupby(['NO'], sort = False): | |
| 1110 nVehicles += 1 | |
| 1111 positions = array(group['POS']) | |
| 1112 diff = positions[1:]-positions[:-1] | |
| 1113 if npsum(diff == 0.) >= proportionStationaryTime*(len(positions)-1): | |
| 1114 nStationary += 1 | |
| 1115 elif filename.endswith(".sqlite"): | |
| 1116 # select trajectory_id, t, s_coordinate, speed from curvilinear_positions where trajectory_id between 1860 and 1870 and speed < 0.1 | |
| 1117 # pb of the meaning of proportionStationaryTime in arterial network? Why proportion of existence time? | |
| 1118 pass | |
| 1119 else: | |
| 1120 print("File type of "+filename+" not supported (only .sqlite and .fzp files)") | |
| 1121 | |
| 1122 return nStationary, nVehicles | |
| 1123 | |
| 1124 def countCollisionsVissim(filename, lanes = None, collisionTimeDifference = 0.2, lowMemory = True): | |
| 1125 '''Counts the number of collisions per lane in a VISSIM trajectory file | |
| 1126 | |
| 1127 To distinguish between cars passing and collision, | |
| 1128 one checks when the sign of the position difference inverts | |
| 1129 (if the time are closer than collisionTimeDifference) | |
| 1130 If lanes is not None, only the data for the selected lanes will be provided | |
| 1131 (format as string x_y where x is link index and y is lane index)''' | |
| 1132 data = read_csv(filename, delimiter=';', comment='*', header=0, skiprows = 1, usecols = ['LANE\\LINK\\NO', 'LANE\\INDEX', '$VEHICLE:SIMSEC', 'NO', 'POS'], low_memory = lowMemory) | |
| 1133 data = selectPDLanes(data, lanes) | |
| 1134 data = data.convert_objects(convert_numeric=True) | |
| 1135 | |
| 1136 merged = merge(data, data, how='inner', left_on=['LANE\\LINK\\NO', 'LANE\\INDEX', '$VEHICLE:SIMSEC'], right_on=['LANE\\LINK\\NO', 'LANE\\INDEX', '$VEHICLE:SIMSEC'], sort = False) | |
| 1137 merged = merged[merged['NO_x']>merged['NO_y']] | |
| 1138 | |
| 1139 nCollisions = 0 | |
| 1140 for name, group in merged.groupby(['LANE\\LINK\\NO', 'LANE\\INDEX', 'NO_x', 'NO_y']): | |
| 1141 diff = group['POS_x']-group['POS_y'] | |
| 1142 # diff = group['POS_x']-group['POS_y'] # to check the impact of convert_objects and the possibility of using type conversion in read_csv or function to convert strings if any | |
| 1143 if len(diff) >= 2 and npmin(diff) < 0 and npmax(diff) > 0: | |
| 1144 xidx = diff[diff < 0].argmax() | |
| 1145 yidx = diff[diff > 0].argmin() | |
| 1146 if abs(group.loc[xidx, '$VEHICLE:SIMSEC'] - group.loc[yidx, '$VEHICLE:SIMSEC']) <= collisionTimeDifference: | |
| 1147 nCollisions += 1 | |
| 1148 | |
| 1149 # select TD1.link_id, TD1.lane_id from temp.diff_positions as TD1, temp.diff_positions as TD2 where TD1.link_id = TD2.link_id and TD1.lane_id = TD2.lane_id and TD1.id1 = TD2.id1 and TD1.id2 = TD2.id2 and TD1.t = TD2.t+0.1 and TD1.diff*TD2.diff < 0; # besoin de faire un group by?? | |
| 1150 # create temp table diff_positions as select CP1.t as t, CP1.link_id as link_id, CP1.lane_id as lane_id, CP1.trajectory_id as id1, CP2.trajectory_id as id2, CP1.s_coordinate - CP2.s_coordinate as diff from curvilinear_positions CP1, curvilinear_positions CP2 where CP1.link_id = CP2.link_id and CP1.lane_id = CP2.lane_id and CP1.t = CP2.t and CP1.trajectory_id > CP2.trajectory_id; | |
| 1151 # SQL select link_id, lane_id, id1, id2, min(diff), max(diff) from (select CP1.t as t, CP1.link_id as link_id, CP1.lane_id as lane_id, CP1.trajectory_id as id1, CP2.trajectory_id as id2, CP1.s_coordinate - CP2.s_coordinate as diff from curvilinear_positions CP1, curvilinear_positions CP2 where CP1.link_id = CP2.link_id and CP1.lane_id = CP2.lane_id and CP1.t = CP2.t and CP1.trajectory_id > CP2.trajectory_id) group by link_id, lane_id, id1, id2 having min(diff)*max(diff) < 0 | |
| 1152 return nCollisions | |
| 1153 | |
| 1154 def loadTrajectoriesFromNgsimFile(filename, nObjects = -1, sequenceNum = -1): | |
| 1155 '''Reads data from the trajectory data provided by NGSIM project | |
| 1156 and returns the list of Feature objects''' | |
| 1157 objects = [] | |
| 1158 | |
| 1159 inputfile = openCheck(filename, quitting = True) | |
| 1160 | |
| 1161 def createObject(numbers): | |
| 1162 firstFrameNum = int(numbers[1]) | |
| 1163 # do the geometry and usertype | |
| 1164 | |
| 1165 firstFrameNum = int(numbers[1]) | |
| 1166 lastFrameNum = firstFrameNum+int(numbers[2])-1 | |
| 1167 #time = moving.TimeInterval(firstFrameNum, firstFrameNum+int(numbers[2])-1) | |
| 1168 obj = moving.MovingObject(num = int(numbers[0]), | |
| 1169 timeInterval = moving.TimeInterval(firstFrameNum, lastFrameNum), | |
| 1170 positions = moving.Trajectory([[float(numbers[6])],[float(numbers[7])]]), | |
| 1171 userType = int(numbers[10])) | |
| 1172 obj.userType = int(numbers[10]) | |
| 1173 obj.laneNums = [int(numbers[13])] | |
| 1174 obj.precedingVehicles = [int(numbers[14])] # lead vehicle (before) | |
| 1175 obj.followingVehicles = [int(numbers[15])] # following vehicle (after) | |
| 1176 obj.spaceHeadways = [float(numbers[16])] # feet | |
| 1177 obj.timeHeadways = [float(numbers[17])] # seconds | |
| 1178 obj.curvilinearPositions = moving.CurvilinearTrajectory([float(numbers[5])],[float(numbers[4])], obj.laneNums) # X is the longitudinal coordinate | |
| 1179 obj.speeds = [float(numbers[11])] | |
| 1180 obj.size = [float(numbers[8]), float(numbers[9])] # 8 lengh, 9 width # TODO: temporary, should use a geometry object | |
| 1181 return obj | |
| 1182 | |
| 1183 numbers = readline(inputfile).strip().split() | |
| 1184 if (len(numbers) > 0): | |
| 1185 obj = createObject(numbers) | |
| 1186 | |
| 1187 for line in inputfile: | |
| 1188 numbers = line.strip().split() | |
| 1189 if obj.getNum() != int(numbers[0]): | |
| 1190 # check and adapt the length to deal with issues in NGSIM data | |
| 1191 if (obj.length() != obj.positions.length()): | |
| 1192 print('length pb with object {} ({},{})'.format(obj.getNum(),obj.length(),obj.positions.length())) | |
| 1193 obj.last = obj.getFirstInstant()+obj.positions.length()-1 | |
| 1194 #obj.velocities = utils.computeVelocities(f.positions) # compare norm to speeds ? | |
| 1195 objects.append(obj) | |
| 1196 if (nObjects>0) and (len(objects)>=nObjects): | |
| 1197 break | |
| 1198 obj = createObject(numbers) | |
| 1199 else: | |
| 1200 obj.laneNums.append(int(numbers[13])) | |
| 1201 obj.positions.addPositionXY(float(numbers[6]), float(numbers[7])) | |
| 1202 obj.curvilinearPositions.addPositionSYL(float(numbers[5]), float(numbers[4]), obj.laneNums[-1]) | |
| 1203 obj.speeds.append(float(numbers[11])) | |
| 1204 obj.precedingVehicles.append(int(numbers[14])) | |
| 1205 obj.followingVehicles.append(int(numbers[15])) | |
| 1206 obj.spaceHeadways.append(float(numbers[16])) | |
| 1207 obj.timeHeadways.append(float(numbers[17])) | |
| 1208 | |
| 1209 if (obj.size[0] != float(numbers[8])): | |
| 1210 print('changed length obj {}'.format(obj.getNum())) | |
| 1211 if (obj.size[1] != float(numbers[9])): | |
| 1212 print('changed width obj {}'.format(obj.getNum())) | |
| 1213 | |
| 1214 inputfile.close() | |
| 1215 return objects | |
| 1216 | |
| 1217 def convertNgsimFile(inputfile, outputfile, append = False, nObjects = -1, sequenceNum = 0): | |
| 1218 '''Reads data from the trajectory data provided by NGSIM project | |
| 1219 and converts to our current format.''' | |
| 1220 if append: | |
| 1221 out = openCheck(outputfile,'a') | |
| 1222 else: | |
| 1223 out = openCheck(outputfile,'w') | |
| 1224 nObjectsPerType = [0,0,0] | |
| 1225 | |
| 1226 features = loadNgsimFile(inputfile, sequenceNum) | |
| 1227 for f in features: | |
| 1228 nObjectsPerType[f.userType-1] += 1 | |
| 1229 f.write(out) | |
| 1230 | |
| 1231 print(nObjectsPerType) | |
| 1232 | |
| 1233 out.close() | |
| 1234 | |
| 1235 def loadPinholeCameraModel(filename, tanalystFormat = True): | |
| 1236 '''Loads the data from a file containing the camera parameters | |
| 1237 (pinhole camera model, http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html) | |
| 1238 and returns a dictionary''' | |
| 1239 if tanalystFormat: | |
| 1240 f = openCheck(filename, quitting = True) | |
| 1241 content = getLines(f) | |
| 1242 cameraData = {} | |
| 1243 for l in content: | |
| 1244 tmp = l.split(':') | |
| 1245 cameraData[tmp[0]] = float(tmp[1].strip().replace(',','.')) | |
| 1246 return cameraData | |
| 1247 else: | |
| 1248 print('Unknown camera model (not tanalyst format') | |
| 1249 return None | |
| 1250 | |
| 1251 def savePositionsToCsv(f, obj): | |
| 1252 timeInterval = obj.getTimeInterval() | |
| 1253 positions = obj.getPositions() | |
| 1254 curvilinearPositions = obj.getCurvilinearPositions() | |
| 1255 for i in range(int(obj.length())): | |
| 1256 p1 = positions[i] | |
| 1257 s = '{},{},{},{}'.format(obj.num,timeInterval[i],p1.x,p1.y) | |
| 1258 if curvilinearPositions is not None: | |
| 1259 p2 = curvilinearPositions[i] | |
| 1260 s += ',{},{}'.format(p2[0],p2[1]) | |
| 1261 f.write(s+'\n') | |
| 1262 | |
| 1263 def saveTrajectoriesToCsv(filename, objects): | |
| 1264 f = openCheck(filename, 'w') | |
| 1265 for i,obj in enumerate(objects): | |
| 1266 savePositionsToCsv(f, obj) | |
| 1267 f.close() | |
| 1268 | |
| 1269 | |
| 1270 ######################### | |
| 1271 # Utils to read .ini type text files for configuration, meta data... | |
| 1272 ######################### | |
| 1273 | |
| 1274 class ClassifierParameters(VideoFilenameAddable): | |
| 1275 'Class for the parameters of object classifiers' | |
| 1276 def loadConfigFile(self, filename): | |
| 1277 from configparser import ConfigParser | |
| 1278 | |
| 1279 config = ConfigParser() | |
| 1280 config.read_file(addSectionHeader(openCheck(filename))) | |
| 1281 | |
| 1282 parentPath = Path(filename).parent | |
| 1283 self.sectionHeader = config.sections()[0] | |
| 1284 | |
| 1285 self.pedBikeCarSVMFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'pbv-svm-filename')) | |
| 1286 self.bikeCarSVMFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'bv-svm-filename')) | |
| 1287 self.percentIncreaseCrop = config.getfloat(self.sectionHeader, 'percent-increase-crop') | |
| 1288 self.minNPixels = config.getint(self.sectionHeader, 'min-npixels-crop') | |
| 1289 x = config.getint(self.sectionHeader, 'hog-rescale-size') | |
| 1290 self.hogRescaleSize = (x, x) | |
| 1291 self.hogNOrientations = config.getint(self.sectionHeader, 'hog-norientations') | |
| 1292 x = config.getint(self.sectionHeader, 'hog-npixels-cell') | |
| 1293 self.hogNPixelsPerCell = (x, x) | |
| 1294 x = config.getint(self.sectionHeader, 'hog-ncells-block') | |
| 1295 self.hogNCellsPerBlock = (x, x) | |
| 1296 self.hogBlockNorm = config.get(self.sectionHeader, 'hog-block-norm') | |
| 1297 | |
| 1298 self.speedAggregationMethod = config.get(self.sectionHeader, 'speed-aggregation-method') | |
| 1299 self.nFramesIgnoreAtEnds = config.getint(self.sectionHeader, 'nframes-ignore-at-ends') | |
| 1300 self.speedAggregationCentile = config.getint(self.sectionHeader, 'speed-aggregation-centile') | |
| 1301 self.minSpeedEquiprobable = config.getfloat(self.sectionHeader, 'min-speed-equiprobable') | |
| 1302 self.maxPercentUnknown = config.getfloat(self.sectionHeader, 'max-prop-unknown-appearance') | |
| 1303 self.maxPedestrianSpeed = config.getfloat(self.sectionHeader, 'max-ped-speed') | |
| 1304 self.maxCyclistSpeed = config.getfloat(self.sectionHeader, 'max-cyc-speed') | |
| 1305 self.meanPedestrianSpeed = config.getfloat(self.sectionHeader, 'mean-ped-speed') | |
| 1306 self.stdPedestrianSpeed = config.getfloat(self.sectionHeader, 'std-ped-speed') | |
| 1307 self.locationCyclistSpeed = config.getfloat(self.sectionHeader, 'cyc-speed-loc') | |
| 1308 self.scaleCyclistSpeed = config.getfloat(self.sectionHeader, 'cyc-speed-scale') | |
| 1309 self.meanVehicleSpeed = config.getfloat(self.sectionHeader, 'mean-veh-speed') | |
| 1310 self.stdVehicleSpeed = config.getfloat(self.sectionHeader, 'std-veh-speed') | |
| 1311 | |
| 1312 def __init__(self, filename = None): | |
| 1313 if filename is not None and Path(filename).exists(): | |
| 1314 self.loadConfigFile(filename) | |
| 1315 else: | |
| 1316 print('Configuration filename {} could not be loaded.'.format(filename)) | |
| 1317 | |
| 1318 def convertToFrames(self, frameRate, speedRatio = 3.6): | |
| 1319 '''Converts parameters with a relationship to time in 'native' frame time | |
| 1320 speedRatio is the conversion from the speed unit in the config file | |
| 1321 to the distance per second | |
| 1322 | |
| 1323 ie param(config file) = speedRatio x fps x param(used in program) | |
| 1324 eg km/h = 3.6 (m/s to km/h) x frame/s x m/frame''' | |
| 1325 denominator = frameRate*speedRatio | |
| 1326 #denominator2 = denominator**2 | |
| 1327 self.minSpeedEquiprobable = self.minSpeedEquiprobable/denominator | |
| 1328 self.maxPedestrianSpeed = self.maxPedestrianSpeed/denominator | |
| 1329 self.maxCyclistSpeed = self.maxCyclistSpeed/denominator | |
| 1330 self.meanPedestrianSpeed = self.meanPedestrianSpeed/denominator | |
| 1331 self.stdPedestrianSpeed = self.stdPedestrianSpeed/denominator | |
| 1332 self.meanVehicleSpeed = self.meanVehicleSpeed/denominator | |
| 1333 self.stdVehicleSpeed = self.stdVehicleSpeed/denominator | |
| 1334 # special case for the lognormal distribution | |
| 1335 self.locationCyclistSpeed = self.locationCyclistSpeed-log(denominator) | |
| 1336 #self.scaleCyclistSpeed = self.scaleCyclistSpeed # no modification of scale | |
| 1337 | |
| 1338 | |
| 1339 class ProcessParameters(VideoFilenameAddable): | |
| 1340 '''Class for all parameters controlling data processing: input, | |
| 1341 method parameters, etc. for tracking and safety | |
| 1342 | |
| 1343 Note: framerate is already taken into account''' | |
| 1344 | |
| 1345 def loadConfigFile(self, filename): | |
| 1346 from configparser import ConfigParser | |
| 1347 | |
| 1348 config = ConfigParser(strict=False) | |
| 1349 config.read_file(addSectionHeader(openCheck(filename))) | |
| 1350 | |
| 1351 parentPath = Path(filename).parent | |
| 1352 self.sectionHeader = config.sections()[0] | |
| 1353 # Tracking/display parameters | |
| 1354 self.videoFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'video-filename')) | |
| 1355 self.databaseFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'database-filename')) | |
| 1356 self.homographyFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'homography-filename')) | |
| 1357 if Path(self.homographyFilename).exists(): | |
| 1358 self.homography = loadtxt(self.homographyFilename) | |
| 1359 else: | |
| 1360 self.homography = None | |
| 1361 self.intrinsicCameraFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'intrinsic-camera-filename')) | |
| 1362 if Path(self.intrinsicCameraFilename).exists(): | |
| 1363 self.intrinsicCameraMatrix = loadtxt(self.intrinsicCameraFilename) | |
| 1364 else: | |
| 1365 self.intrinsicCameraMatrix = None | |
| 1366 distortionCoefficients = getValuesFromINIFile(filename, 'distortion-coefficients', '=') | |
| 1367 self.distortionCoefficients = [float(x) for x in distortionCoefficients] | |
| 1368 self.undistortedImageMultiplication = config.getfloat(self.sectionHeader, 'undistorted-size-multiplication') | |
| 1369 self.undistort = config.getboolean(self.sectionHeader, 'undistort') | |
| 1370 self.firstFrameNum = config.getint(self.sectionHeader, 'frame1') | |
| 1371 self.videoFrameRate = config.getfloat(self.sectionHeader, 'video-fps') | |
| 1372 | |
| 1373 self.minFeatureTime = config.getfloat(self.sectionHeader, 'min-feature-time') | |
| 1374 | |
| 1375 self.classifierFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'classifier-filename')) | |
| 1376 | |
| 1377 # Safety parameters | |
| 1378 self.maxPredictedSpeed = config.getfloat(self.sectionHeader, 'max-predicted-speed')/3.6/self.videoFrameRate | |
| 1379 self.predictionTimeHorizon = config.getfloat(self.sectionHeader, 'prediction-time-horizon')*self.videoFrameRate | |
| 1380 self.collisionDistance = config.getfloat(self.sectionHeader, 'collision-distance') | |
| 1381 self.crossingZones = config.getboolean(self.sectionHeader, 'crossing-zones') | |
| 1382 self.predictionMethod = config.get(self.sectionHeader, 'prediction-method') | |
| 1383 self.nPredictedTrajectories = config.getint(self.sectionHeader, 'npredicted-trajectories') | |
| 1384 self.maxNormalAcceleration = config.getfloat(self.sectionHeader, 'max-normal-acceleration')/self.videoFrameRate**2 | |
| 1385 self.maxNormalSteering = config.getfloat(self.sectionHeader, 'max-normal-steering')/self.videoFrameRate | |
| 1386 self.minExtremeAcceleration = config.getfloat(self.sectionHeader, 'min-extreme-acceleration')/self.videoFrameRate**2 | |
| 1387 self.maxExtremeAcceleration = config.getfloat(self.sectionHeader, 'max-extreme-acceleration')/self.videoFrameRate**2 | |
| 1388 self.maxExtremeSteering = config.getfloat(self.sectionHeader, 'max-extreme-steering')/self.videoFrameRate | |
| 1389 self.useFeaturesForPrediction = config.getboolean(self.sectionHeader, 'use-features-prediction') | |
| 1390 self.constantSpeedPrototypePrediction = config.getboolean(self.sectionHeader, 'constant-speed') | |
| 1391 self.maxLcssDistance = config.getfloat(self.sectionHeader, 'max-lcss-distance') | |
| 1392 self.lcssMetric = config.get(self.sectionHeader, 'lcss-metric') | |
| 1393 self.minLcssSimilarity = config.getfloat(self.sectionHeader, 'min-lcss-similarity') | |
| 1394 | |
| 1395 def __init__(self, filename = None): | |
| 1396 if filename is not None and Path(filename).exists(): | |
| 1397 self.loadConfigFile(filename) | |
| 1398 else: | |
| 1399 print('Configuration filename {} could not be loaded.'.format(filename)) | |
| 1400 | |
| 1401 def processVideoArguments(args): | |
| 1402 '''Loads information from configuration file | |
| 1403 then checks what was passed on the command line | |
| 1404 for override (eg video filename and database filename''' | |
| 1405 parentPath = Path(args.configFilename).parent | |
| 1406 if args.configFilename is not None: # consider there is a configuration file | |
| 1407 params = ProcessParameters(args.configFilename) | |
| 1408 videoFilename = params.videoFilename | |
| 1409 databaseFilename = params.databaseFilename | |
| 1410 if params.homography is not None: | |
| 1411 invHomography = linalg.inv(params.homography) | |
| 1412 else: | |
| 1413 invHomography = None | |
| 1414 intrinsicCameraMatrix = params.intrinsicCameraMatrix | |
| 1415 distortionCoefficients = array(params.distortionCoefficients) | |
| 1416 undistortedImageMultiplication = params.undistortedImageMultiplication | |
| 1417 undistort = params.undistort | |
| 1418 firstFrameNum = params.firstFrameNum | |
| 1419 else: | |
| 1420 invHomography = None | |
| 1421 undistort = False | |
| 1422 intrinsicCameraMatrix = None | |
| 1423 distortionCoefficients = [] | |
| 1424 undistortedImageMultiplication = None | |
| 1425 undistort = False | |
| 1426 firstFrameNum = 0 | |
| 1427 | |
| 1428 # override video and database filenames if present on command line | |
| 1429 # if not absolute, make all filenames relative to the location of the configuration filename | |
| 1430 if args.videoFilename is not None: | |
| 1431 videoFilename = args.videoFilename | |
| 1432 else: | |
| 1433 videoFilename = params.videoFilename | |
| 1434 if args.databaseFilename is not None: | |
| 1435 databaseFilename = args.databaseFilename | |
| 1436 else: | |
| 1437 databaseFilename = params.databaseFilename | |
| 1438 | |
| 1439 return params, videoFilename, databaseFilename, invHomography, intrinsicCameraMatrix, distortionCoefficients, undistortedImageMultiplication, undistort, firstFrameNum | |
| 1440 | |
| 1441 # deprecated | |
| 1442 class SceneParameters(object): | |
| 1443 def __init__(self, config, sectionName): | |
| 1444 from configparser import NoOptionError | |
| 1445 from ast import literal_eval | |
| 1446 try: | |
| 1447 self.sitename = config.get(sectionName, 'sitename') | |
| 1448 self.databaseFilename = config.get(sectionName, 'data-filename') | |
| 1449 self.homographyFilename = config.get(sectionName, 'homography-filename') | |
| 1450 self.calibrationFilename = config.get(sectionName, 'calibration-filename') | |
| 1451 self.videoFilename = config.get(sectionName, 'video-filename') | |
| 1452 self.frameRate = config.getfloat(sectionName, 'framerate') | |
| 1453 self.date = datetime.strptime(config.get(sectionName, 'date'), datetimeFormat) # 2011-06-22 11:00:39 | |
| 1454 self.translation = literal_eval(config.get(sectionName, 'translation')) # = [0.0, 0.0] | |
| 1455 self.rotation = config.getfloat(sectionName, 'rotation') | |
| 1456 self.duration = config.getint(sectionName, 'duration') | |
| 1457 except NoOptionError as e: | |
| 1458 print(e) | |
| 1459 print('Not a section for scene meta-data') | |
| 1460 | |
| 1461 @staticmethod | |
| 1462 def loadConfigFile(filename): | |
| 1463 from configparser import ConfigParser | |
| 1464 config = ConfigParser() | |
| 1465 config.readfp(openCheck(filename)) | |
| 1466 configDict = dict() | |
| 1467 for sectionName in config.sections(): | |
| 1468 configDict[sectionName] = SceneParameters(config, sectionName) | |
| 1469 return configDict | |
| 1470 | |
| 1471 | |
| 1472 if __name__ == "__main__": | |
| 1473 import doctest | |
| 1474 import unittest | |
| 1475 suite = doctest.DocFileSuite('tests/storage.txt') | |
| 1476 unittest.TextTestRunner().run(suite) | |
| 1477 # #doctest.testmod() | |
| 1478 # #doctest.testfile("example.txt") |
