Mercurial Hosting > traffic-intelligence
changeset 1077:3939ae415be0
Merging
author | Wendlasida |
---|---|
date | Fri, 20 Jul 2018 14:03:34 -0400 |
parents | 108c5dc4e34a (current diff) 0154133e77df (diff) |
children | 8cc3feb1c1c5 |
files | ..hgignore.swp trafficintelligence/moving.py trafficintelligence/storage.py |
diffstat | 11 files changed, 380 insertions(+), 241 deletions(-) [+] |
line wrap: on
line diff
--- a/c/Motion.cpp Fri Jul 20 13:50:43 2018 -0400 +++ b/c/Motion.cpp Fri Jul 20 14:03:34 2018 -0400 @@ -53,7 +53,7 @@ float disp = 0; for (unsigned int i=0; i<nDisplacements; i++) disp += displacementDistances[nPositions-2-i]; - result = disp <= minTotalFeatureDisplacement; + result = disp < minTotalFeatureDisplacement; } return result; }
--- a/scripts/compute-homography.py Fri Jul 20 13:50:43 2018 -0400 +++ b/scripts/compute-homography.py Fri Jul 20 14:03:34 2018 -0400 @@ -30,7 +30,7 @@ parser.add_argument('--display', dest = 'displayPoints', help = 'display original and projected points on both images', action = 'store_true') parser.add_argument('--intrinsic', dest = 'intrinsicCameraMatrixFilename', help = 'name of the intrinsic camera file') parser.add_argument('--distortion-coefficients', dest = 'distortionCoefficients', help = 'distortion coefficients', nargs = '*', type = float) -parser.add_argument('--undistorted-multiplication', dest = 'undistortedImageMultiplication', help = 'undistorted image multiplication', type = float) +parser.add_argument('--undistorted-multiplication', dest = 'undistortedImageMultiplication', help = 'undistorted image multiplication', type = float, default = 1.) parser.add_argument('--undistort', dest = 'undistort', help = 'undistort the video (because features have been extracted that way', action = 'store_true') parser.add_argument('--save', dest = 'saveImages', help = 'save the undistorted video frame (display option must be chosen)', action = 'store_true')
--- a/scripts/process.py Fri Jul 20 13:50:43 2018 -0400 +++ b/scripts/process.py Fri Jul 20 14:03:34 2018 -0400 @@ -8,7 +8,7 @@ #atplotlib.use('Agg') import matplotlib.pyplot as plt import numpy as np -from pandas import DataFrame +import pandas as pd from trafficintelligence import storage, events, prediction, cvutils, utils, moving, processing, ml from trafficintelligence.metadata import * @@ -16,14 +16,15 @@ parser = argparse.ArgumentParser(description='This program manages the processing of several files based on a description of the sites and video data in an SQLite database following the metadata module.') # input parser.add_argument('--db', dest = 'metadataFilename', help = 'name of the metadata file', required = True) -parser.add_argument('--videos', dest = 'videoIds', help = 'indices of the video sequences', nargs = '*', type = int) +parser.add_argument('--videos', dest = 'videoIds', help = 'indices of the video sequences', nargs = '*') parser.add_argument('--sites', dest = 'siteIds', help = 'indices of the video sequences', nargs = '*') # main function parser.add_argument('--delete', dest = 'delete', help = 'data to delete', choices = ['feature', 'object', 'classification', 'interaction']) parser.add_argument('--process', dest = 'process', help = 'data to process', choices = ['feature', 'object', 'classification', 'prototype', 'interaction']) parser.add_argument('--display', dest = 'display', help = 'data to display (replay over video)', choices = ['feature', 'object', 'classification', 'interaction']) -parser.add_argument('--analyze', dest = 'analyze', help = 'data to analyze (results)', choices = ['feature', 'object', 'classification', 'interaction']) +parser.add_argument('--progress', dest = 'progress', help = 'information about the progress of processing', action = 'store_true') +parser.add_argument('--analyze', dest = 'analyze', help = 'data to analyze (results)', choices = ['feature', 'object', 'classification', 'interaction', 'event']) # common options parser.add_argument('--cfg', dest = 'configFilename', help = 'name of the configuration file') @@ -60,11 +61,13 @@ # analysis options parser.add_argument('--output', dest = 'output', help = 'kind of output to produce (interval means)', choices = ['figure', 'interval', 'event']) parser.add_argument('--min-user-duration', dest = 'minUserDuration', help = 'mininum duration we have to see the user to take into account in the analysis (s)', type = float, default = 0.1) -parser.add_argument('--interval-duration', dest = 'intervalDuration', help = 'length of time interval to aggregate data (min)', type = float, default = 15.) -parser.add_argument('--aggregation', dest = 'aggMethod', help = 'aggregation method per user/event and per interval', choices = ['mean', 'median', 'centile'], nargs = '*', default = ['median']) -parser.add_argument('--aggregation-centile', dest = 'aggCentiles', help = 'centile(s) to compute from the observations', nargs = '*', type = int) +parser.add_argument('--interval-duration', dest = 'intervalDuration', help = 'length of time interval to aggregate data (min)', type = int, default = 15) +parser.add_argument('--aggregation', dest = 'aggMethods', help = 'aggregation method per user/interaction and per interval', choices = ['mean', 'median', 'centile'], nargs = '*', default = ['median']) +parser.add_argument('--aggregation-centiles', dest = 'aggCentiles', help = 'centile(s) to compute from the observations', nargs = '*', type = int) +parser.add_argument('--event-thresholds', dest = 'eventThresholds', help = 'threshold to count severe situations', nargs = '*', type = float) +parser.add_argument('--event-filename', dest = 'eventFilename', help = 'filename of the event data') dpi = 150 -# unit of analysis: site or video sequence? +# unit of analysis: site - camera-view # need way of selecting sites as similar as possible to sql alchemy syntax # override tracking.cfg from db @@ -82,16 +85,22 @@ videoSequences = [] sites = [] if args.videoIds is not None: - videoSequences = [session.query(VideoSequence).get(videoId) for videoId in args.videoIds] - siteIds = set([vs.cameraView.siteIdx for vs in videoSequences]) + for videoId in args.videoIds: + if '-' in videoId: + videoSequences.extend([session.query(VideoSequence).get(i) for i in moving.TimeInterval.parse(videoId)]) + else: + videoSequences.append(session.query(VideoSequence).get(int(videoId))) + videoSequences = [vs for vs in videoSequences if vs is not None] + sites = set([vs.cameraView.site for vs in videoSequences]) elif args.siteIds is not None: - siteIds = set(args.siteIds) - for siteId in siteIds: - tmpsites = getSite(session, siteId) - sites.extend(tmpsites) - for site in tmpsites: - for cv in site.cameraViews: - videoSequences.extend(cv.videoSequences) + for siteId in args.siteIds: + if '-' in siteId: + sites.extend([session.query(Site).get(i) for i in moving.TimeInterval.parse(siteId)]) + else: + sites.append(session.query(Site).get(int(siteId))) + sites = [s for s in sites if s is not None] + for site in sites: + videoSequences.extend(getSiteVideoSequences(site)) else: print('No video/site to process') @@ -99,6 +108,34 @@ pool = Pool(args.nProcesses) ################################# +# Report progress in the processing +################################# +if args.progress: + print('Providing information on data progress') + headers = ['site', 'vs', 'features', 'objects', 'interactions'] # todo add prototypes and object classification + data = [] + for site in sites: + unprocessedVideoSequences = [] + for vs in getSiteVideoSequences(site): + if (parentPath/vs.getDatabaseFilename()).is_file(): # TODO check time of file? + tableNames = storage.tableNames(str(parentPath.absolute()/vs.getDatabaseFilename())) + data.append([site.name, vs.idx, 'positions' in tableNames, 'objects' in tableNames, 'interactions' in tableNames]) + else: + unprocessedVideoSequences.append(vs) + data.append([site.name, vs.idx, False, False, False]) + #if len(unprocessedVideoSequences): + # print('Site {} ({}) has {} completely unprocessed video sequences'.format (site.name, site.idx, len(unprocessedVideoSequences))) + data = pd.DataFrame(data, columns = headers) + print('-'*80) + print('\t'+' '.join(headers[2:])) + print('-'*80) + for name, group in data.groupby(['site']): #.agg({'vs': 'count'})) + n = group.vs.count() + print('{}: {} % / {} % / {} % ({})'.format(name, 100*group.features.sum()/float(n), 100*group.objects.sum()/float(n), 100*group.interactions.sum()/float(n), n)) + print('-'*80) + print(data) + +################################# # Delete ################################# if args.delete is not None: @@ -119,20 +156,20 @@ if args.process in ['feature', 'object']: # tracking if args.nProcesses == 1: for vs in videoSequences: - if not (parentPath/vs.getDatabaseFilename()).exists() or args.process == 'object': + if not (parentPath/vs.getDatabaseFilename()).is_file() or args.process == 'object': if args.configFilename is None: configFilename = str(parentPath/vs.cameraView.getTrackingConfigurationFilename()) else: configFilename = args.configFilename if vs.cameraView.cameraType is None: cvutils.tracking(configFilename, args.process == 'object', str(parentPath.absolute()/vs.getVideoSequenceFilename()), str(parentPath.absolute()/vs.getDatabaseFilename()), str(parentPath.absolute()/vs.cameraView.getHomographyFilename()), str(parentPath.absolute()/vs.cameraView.getMaskFilename()), False, None, None, args.dryRun) - else: + else: #caution: cameratype can be not none, but without parameters for undistortion cvutils.tracking(configFilename, args.process == 'object', str(parentPath.absolute()/vs.getVideoSequenceFilename()), str(parentPath.absolute()/vs.getDatabaseFilename()), str(parentPath.absolute()/vs.cameraView.getHomographyFilename()), str(parentPath.absolute()/vs.cameraView.getMaskFilename()), True, vs.cameraView.cameraType.intrinsicCameraMatrix, vs.cameraView.cameraType.distortionCoefficients, args.dryRun) else: print('SQLite already exists: {}'.format(parentPath/vs.getDatabaseFilename())) else: for vs in videoSequences: - if not (parentPath/vs.getDatabaseFilename()).exists() or args.process == 'object': + if not (parentPath/vs.getDatabaseFilename()).is_file() or args.process == 'object': if args.configFilename is None: configFilename = str(parentPath/vs.cameraView.getTrackingConfigurationFilename()) else: @@ -147,7 +184,7 @@ pool.join() elif args.process == 'prototype': # motion pattern learning - # learn by site by default -> group videos by site (or by camera view? TODO add cameraviews) + # learn by site by default -> group videos by camera view TODO # by default, load all objects, learn and then assign (BUT not save the assignments) for site in sites: print('Learning motion patterns for site {} ({})'.format(site.idx, site.name)) @@ -177,7 +214,6 @@ outputPrototypeDatabaseFilename = args.databaseFilename else: outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename - # TODO maintain mapping from object prototype to db filename + compute nmatchings before clusterSizes = ml.computeClusterSizes(labels, prototypeIndices, -1) storage.savePrototypesToSqlite(str(parentPath/site.getPath()/outputPrototypeDatabaseFilename), [moving.Prototype(object2VideoSequences[trainingObjects[i]].getDatabaseFilename(False), trainingObjects[i].getNum(), prototypeType, clusterSizes[i]) for i in prototypeIndices]) @@ -213,46 +249,34 @@ if args.analyze == 'object': # user speeds, accelerations # aggregation per site + if args.eventFilename is None: + print('Missing output filename (event-filename). Exiting') + sys.exit(0) data = [] # list of observation per site-user with time - headers = ['sites', 'date', 'time', 'user_type'] - aggFunctions = {} - for method in args.aggMethod: - if method == 'centile': - aggFunctions[method] = utils.aggregationFunction(method, args.aggCentiles) - for c in args.aggCentiles: - headers.append('{}{}'.format(method,c)) - else: - aggFunctions[method] = utils.aggregationFunction(method) - headers.append(method) - for vs in videoSequences: - d = vs.startTime.date() - t1 = vs.startTime.time() - minUserDuration = args.minUserDuration*vs.cameraView.cameraType.frameRate - print('Extracting speed from '+vs.getDatabaseFilename()) - objects = storage.loadTrajectoriesFromSqlite(str(parentPath/vs.getDatabaseFilename()), 'object', args.nObjects) - for o in objects: - if o.length() > minUserDuration: - row = [vs.cameraView.siteIdx, d, utils.framesToTime(o.getFirstInstant(), vs.cameraView.cameraType.frameRate, t1), o.getUserType()] - tmp = o.getSpeeds() - for method,func in aggFunctions.items(): - aggSpeeds = vs.cameraView.cameraType.frameRate*3.6*func(tmp) - if method == 'centile': - row += aggSpeeds.tolist() - else: - row.append(aggSpeeds) - data.append(row) - data = DataFrame(data, columns = headers) + headers = ['site', 'date', 'time', 'user_type'] + aggFunctions, tmpheaders = utils.aggregationMethods(args.aggMethods, args.aggCentiles) + headers.extend(tmpheaders) + if args.nProcesses == 1: + for vs in videoSequences: + data.extend(processing.extractVideoSequenceSpeeds(str(parentPath/vs.getDatabaseFilename()), vs.cameraView.site.name, args.nObjects, vs.startTime, vs.cameraView.cameraType.frameRate, args.minUserDuration, args.aggMethods, args.aggCentiles)) + else: + jobs = [pool.apply_async(processing.extractVideoSequenceSpeeds, args = (str(parentPath/vs.getDatabaseFilename()), vs.cameraView.site.name, args.nObjects, vs.startTime, vs.cameraView.cameraType.frameRate, args.minUserDuration, args.aggMethods, args.aggCentiles)) for vs in videoSequences] + for job in jobs: + data.extend(job.get()) + pool.close() + data = pd.DataFrame(data, columns = headers) if args.output == 'figure': for name in headers[4:]: plt.ioff() plt.figure() - plt.boxplot([data.loc[data['sites']==siteId, name] for siteId in siteIds], labels = [session.query(Site).get(siteId).name for siteId in siteIds]) + plt.boxplot([data.loc[data['site']==site.name, name] for site in sites], labels = [site.name for site in sites]) plt.ylabel(name+' Speeds (km/h)') plt.savefig(name.lower()+'-speeds.png', dpi=dpi) plt.close() elif args.output == 'event': - data.to_csv('speeds.csv', index = False) -if args.analyze == 'interaction': + data.to_csv(args.eventFilename, index = False) + +if args.analyze == 'interaction': # redo as for object, export in dataframe all interaction data indicatorIds = [2,5,7,10] conversionFactors = {2: 1., 5: 30.*3.6, 7:1./30, 10:1./30} maxIndicatorValue = {2: float('inf'), 5: float('inf'), 7:10., 10:10.} @@ -282,3 +306,38 @@ plt.ylabel(events.Interaction.indicatorNames[i]+' ('+events.Interaction.indicatorUnits[i]+')') plt.savefig(events.Interaction.indicatorNames[i]+'.png', dpi=150) plt.close() + +if args.analyze == 'event': # aggregate event data by 15 min interval (args.intervalDuration), count events with thresholds + data = pd.read_csv(args.eventFilename, parse_dates = [2]) + #data = pd.read_csv('./speeds.csv', converters = {'time': lambda s: datetime.datetime.strptime(s, "%H:%M:%S").time()}, nrows = 5000) + # create time for end of each 15 min, then group by, using the agg method for each data column + headers = ['site', 'date', 'intervalend15', 'duration', 'count'] + aggFunctions, tmpheaders = utils.aggregationMethods(args.aggMethods, args.aggCentiles) + dataColumns = list(data.columns[4:]) + for h in dataColumns: + for h2 in tmpheaders: + headers.append(h+'-'+h2) + for h in dataColumns: + for t in args.eventThresholds: + headers.append('n-{}-{}'.format(h, t)) + data['intervalend15'] = data.time.apply(lambda t: (pd.Timestamp(year = t.year, month = t.month, day = t.day,hour = t.hour, minute = (t.minute // args.intervalDuration)*args.intervalDuration)+pd.Timedelta(minutes = 15)).time()) + outputData = [] + for name, group in data.groupby(['site', 'date', 'intervalend15']): + row = [] + row.extend(name) + groupStartTime = group.time.min() + groupEndTime = group.time.max() + row.append((groupEndTime.minute+1-groupStartTime.minute) % 60)#(name[2].minute*60+name[2].second-groupStartTime.minute*60+groupStartTime.second) % 3600) + row.append(len(group)) + for h in dataColumns: + for method,func in aggFunctions.items(): + aggregated = func(group[h]) + if method == 'centile': + row.extend(aggregated) + else: + row.append(aggregated) + for h in dataColumns: + for t in args.eventThresholds: + row.append((group[h] > t).sum()) + outputData.append(row) + pd.DataFrame(outputData, columns = headers).to_csv(utils.removeExtension(args.eventFilename)+'-aggregated.csv', index = False)
--- a/trafficintelligence/metadata.py Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/metadata.py Fri Jul 20 14:03:34 2018 -0400 @@ -366,6 +366,9 @@ 'Returns the site(s) matching the index' return session.query(CameraView).filter(CameraView.idx == int(viewId)).first() +def getSiteVideoSequences(site): + return [vs for cv in site.cameraViews for vs in cv.videoSequences] + def initializeSites(session, directoryName, nViewsPerSite = 1): '''Initializes default site objects and n camera views per site
--- a/trafficintelligence/moving.py Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/moving.py Fri Jul 20 14:03:34 2018 -0400 @@ -33,7 +33,7 @@ self.last=last def __str__(self): - return '[{0}, {1}]'.format(self.first, self.last) + return '{0}-{1}'.format(self.first, self.last) def __repr__(self): return self.__str__() @@ -69,6 +69,15 @@ self.last += offset @classmethod + def parse(cls, s): + if '-' in s: + tmp = s.split('-') + if len(tmp) == 2: + return cls(int(tmp[0]), int(tmp[1])) # TODO with floats? + print(s+' is not a valid representation of an interval') + return None + + @classmethod def union(cls, interval1, interval2): '''Smallest interval comprising self and interval2''' return cls(min(interval1.first, interval2.first), max(interval1.last, interval2.last))
--- a/trafficintelligence/processing.py Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/processing.py Fri Jul 20 14:03:34 2018 -0400 @@ -3,7 +3,7 @@ import numpy as np -from trafficintelligence import ml +from trafficintelligence import ml, storage, utils def extractSpeeds(objects, zone): speeds = {} @@ -18,6 +18,27 @@ objectsNotInZone.append(o) return speeds, objectsNotInZone +def extractVideoSequenceSpeeds(dbFilename, siteName, nObjects, startTime, frameRate, minUserDurationSeconds, aggMethods, aggCentiles): + data = [] + d = startTime.date() + t1 = startTime.time() + minUserDuration = minUserDurationSeconds*frameRate + print('Extracting speed from '+dbFilename) + aggFunctions, tmpheaders = utils.aggregationMethods(aggMethods, aggCentiles) + objects = storage.loadTrajectoriesFromSqlite(dbFilename, 'object', nObjects) + for o in objects: + if o.length() > minUserDuration: + row = [siteName, d, utils.framesToTime(o.getFirstInstant(), frameRate, t1), o.getUserType()] + tmp = o.getSpeeds() + for method,func in aggFunctions.items(): + aggSpeeds = frameRate*3.6*func(tmp) + if method == 'centile': + row.extend(aggSpeeds.tolist()) + else: + row.append(aggSpeeds) + data.append(row) + return data + def learnAssignMotionPatterns(learn, assign, objects, similarities, minSimilarity, similarityFunc, minClusterSize = 0, optimizeCentroid = False, randomInitialization = False, removePrototypesAfterAssignment = False, initialPrototypes = []): '''Learns motion patterns
--- a/trafficintelligence/storage.py Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/storage.py Fri Jul 20 14:03:34 2018 -0400 @@ -70,6 +70,18 @@ except sqlite3.OperationalError as error: printDBError(error) +def tableNames(filename): + 'Lists the names of the tables in the SQLite file' + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + try: + cursor = connection.cursor() + cursor.execute('SELECT name FROM sqlite_master WHERE type = \'table\'') + return [row[0] for row in cursor] + except sqlite3.OperationalError as error: + printDBError(error) + return [] + def createTrajectoryTable(cursor, tableName): if tableName.endswith('positions') or tableName.endswith('velocities'): 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))") @@ -259,60 +271,61 @@ The number loaded is either the first objectNumbers objects, or the indices in objectNumbers from the database''' objects = [] - with sqlite3.connect(filename) as connection: - objects = loadTrajectoriesFromTable(connection, 'positions', trajectoryType, objectNumbers, timeStep) - objectVelocities = loadTrajectoriesFromTable(connection, 'velocities', trajectoryType, objectNumbers, timeStep) + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + objects = loadTrajectoriesFromTable(connection, 'positions', trajectoryType, objectNumbers, timeStep) + objectVelocities = loadTrajectoriesFromTable(connection, 'velocities', trajectoryType, objectNumbers, timeStep) - if len(objectVelocities) > 0: - for o,v in zip(objects, objectVelocities): - if o.getNum() == v.getNum(): - o.velocities = v.positions - o.velocities.duplicateLastPosition() # avoid having velocity shorter by one position than positions - else: - print('Could not match positions {0} with velocities {1}'.format(o.getNum(), v.getNum())) + if len(objectVelocities) > 0: + for o,v in zip(objects, objectVelocities): + if o.getNum() == v.getNum(): + o.velocities = v.positions + o.velocities.duplicateLastPosition() # avoid having velocity shorter by one position than positions + else: + print('Could not match positions {0} with velocities {1}'.format(o.getNum(), v.getNum())) - if trajectoryType == 'object': - cursor = connection.cursor() - try: - # attribute feature numbers to objects - queryStatement = 'SELECT trajectory_id, object_id FROM objects_features' - if objectNumbers is not None: - queryStatement += ' WHERE object_id '+getObjectCriteria(objectNumbers) - queryStatement += ' ORDER BY object_id' # order is important to group all features per object - logging.debug(queryStatement) - cursor.execute(queryStatement) + if trajectoryType == 'object': + cursor = connection.cursor() + try: + # attribute feature numbers to objects + queryStatement = 'SELECT trajectory_id, object_id FROM objects_features' + if objectNumbers is not None: + queryStatement += ' WHERE object_id '+getObjectCriteria(objectNumbers) + queryStatement += ' ORDER BY object_id' # order is important to group all features per object + logging.debug(queryStatement) + cursor.execute(queryStatement) - featureNumbers = {} - for row in cursor: - objId = row[1] - if objId not in featureNumbers: - featureNumbers[objId] = [row[0]] - else: - featureNumbers[objId].append(row[0]) + featureNumbers = {} + for row in cursor: + objId = row[1] + if objId not in featureNumbers: + featureNumbers[objId] = [row[0]] + else: + featureNumbers[objId].append(row[0]) - for obj in objects: - obj.featureNumbers = featureNumbers[obj.getNum()] + for obj in objects: + obj.featureNumbers = featureNumbers[obj.getNum()] - # load userType - attributes = loadObjectAttributesFromTable(cursor, objectNumbers, True) - for obj in objects: - userType, nObjects = attributes[obj.getNum()] - obj.setUserType(userType) - obj.setNObjects(nObjects) - - # add features - if withFeatures: + # load userType + attributes = loadObjectAttributesFromTable(cursor, objectNumbers, True) for obj in objects: - obj.features = loadTrajectoriesFromSqlite(filename, 'feature', obj.featureNumbers, timeStep = timeStep) - elif nLongestFeaturesPerObject is not None: - for obj in objects: - queryStatement = 'SELECT trajectory_id, max(frame_number)-min(frame_number) AS length FROM positions WHERE trajectory_id '+getObjectCriteria(obj.featureNumbers)+' GROUP BY trajectory_id ORDER BY length DESC' - logging.debug(queryStatement) - cursor.execute(queryStatement) - obj.features = loadTrajectoriesFromSqlite(filename, 'feature', [row[0] for i,row in enumerate(cursor) if i<nLongestFeaturesPerObject], timeStep = timeStep) + userType, nObjects = attributes[obj.getNum()] + obj.setUserType(userType) + obj.setNObjects(nObjects) - except sqlite3.OperationalError as error: - printDBError(error) + # add features + if withFeatures: + for obj in objects: + obj.features = loadTrajectoriesFromSqlite(filename, 'feature', obj.featureNumbers, timeStep = timeStep) + elif nLongestFeaturesPerObject is not None: + for obj in objects: + queryStatement = 'SELECT trajectory_id, max(frame_number)-min(frame_number) AS length FROM positions WHERE trajectory_id '+getObjectCriteria(obj.featureNumbers)+' GROUP BY trajectory_id ORDER BY length DESC' + logging.debug(queryStatement) + cursor.execute(queryStatement) + obj.features = loadTrajectoriesFromSqlite(filename, 'feature', [row[0] for i,row in enumerate(cursor) if i<nLongestFeaturesPerObject], timeStep = timeStep) + + except sqlite3.OperationalError as error: + printDBError(error) return objects def loadObjectFeatureFrameNumbers(filename, objectNumbers = None): @@ -451,19 +464,20 @@ Load descriptions?''' objects = [] - with sqlite3.connect(filename) as connection: - if objectType == 'bb': - topCorners = loadTrajectoriesFromTable(connection, 'bounding_boxes', 'bbtop', objectNumbers, timeStep) - bottomCorners = loadTrajectoriesFromTable(connection, 'bounding_boxes', 'bbbottom', objectNumbers, timeStep) - userTypes = loadObjectAttributesFromTable(connection.cursor(), objectNumbers) # string format is same as object + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + if objectType == 'bb': + topCorners = loadTrajectoriesFromTable(connection, 'bounding_boxes', 'bbtop', objectNumbers, timeStep) + bottomCorners = loadTrajectoriesFromTable(connection, 'bounding_boxes', 'bbbottom', objectNumbers, timeStep) + userTypes = loadObjectAttributesFromTable(connection.cursor(), objectNumbers) # string format is same as object - for t, b in zip(topCorners, bottomCorners): - num = t.getNum() - if t.getNum() == b.getNum(): - annotation = moving.BBMovingObject(num, t.getTimeInterval(), t, b, userTypes[num]) - objects.append(annotation) - else: - print ('Unknown type of bounding box {}'.format(objectType)) + for t, b in zip(topCorners, bottomCorners): + num = t.getNum() + if t.getNum() == b.getNum(): + annotation = moving.BBMovingObject(num, t.getTimeInterval(), t, b, userTypes[num]) + objects.append(annotation) + else: + print ('Unknown type of bounding box {}'.format(objectType)) return objects def saveInteraction(cursor, interaction): @@ -509,29 +523,30 @@ TODO choose the interactions to load''' interactions = [] - with sqlite3.connect(filename) as connection: - cursor = connection.cursor() - try: - 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') - interactionNum = -1 - indicatorTypeNum = -1 - tmpIndicators = {} - for row in cursor: - if row[0] != interactionNum: - interactionNum = row[0] - interactions.append(events.Interaction(interactionNum, moving.TimeInterval(row[3],row[4]), row[1], row[2])) - interactions[-1].indicators = {} - if indicatorTypeNum != row[5] or row[0] != interactionNum: - indicatorTypeNum = row[5] - indicatorName = events.Interaction.indicatorNames[indicatorTypeNum] - indicatorValues = {row[6]:row[7]} - interactions[-1].indicators[indicatorName] = indicators.SeverityIndicator(indicatorName, indicatorValues, mostSevereIsMax = not indicatorName in events.Interaction.timeIndicators) - else: - indicatorValues[row[6]] = row[7] - interactions[-1].indicators[indicatorName].timeInterval.last = row[6] - except sqlite3.OperationalError as error: - printDBError(error) - return [] + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + cursor = connection.cursor() + try: + 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') + interactionNum = -1 + indicatorTypeNum = -1 + tmpIndicators = {} + for row in cursor: + if row[0] != interactionNum: + interactionNum = row[0] + interactions.append(events.Interaction(interactionNum, moving.TimeInterval(row[3],row[4]), row[1], row[2])) + interactions[-1].indicators = {} + if indicatorTypeNum != row[5] or row[0] != interactionNum: + indicatorTypeNum = row[5] + indicatorName = events.Interaction.indicatorNames[indicatorTypeNum] + indicatorValues = {row[6]:row[7]} + interactions[-1].indicators[indicatorName] = indicators.SeverityIndicator(indicatorName, indicatorValues, mostSevereIsMax = not indicatorName in events.Interaction.timeIndicators) + else: + indicatorValues[row[6]] = row[7] + interactions[-1].indicators[indicatorName].timeInterval.last = row[6] + except sqlite3.OperationalError as error: + printDBError(error) + return [] return interactions # load first and last object instants # 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 @@ -554,18 +569,18 @@ def loadBoundingBoxTableForDisplay(filename): '''Loads bounding boxes from bounding_boxes table for display over trajectories''' boundingBoxes = {} # list of bounding boxes for each instant - with sqlite3.connect(filename) as connection: - cursor = connection.cursor() - try: - cursor.execute('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'bounding_boxes\'') - result = cursor.fetchall() - if len(result) > 0: - cursor.execute('SELECT * FROM bounding_boxes') - for row in cursor: - boundingBoxes.setdefault(row[1], []).append([moving.Point(row[2], row[3]), moving.Point(row[4], row[5])]) - except sqlite3.OperationalError as error: - printDBError(error) - return boundingBoxes + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + cursor = connection.cursor() + try: + cursor.execute('SELECT name FROM sqlite_master WHERE type=\'table\' AND name=\'bounding_boxes\'') + result = cursor.fetchall() + if len(result) > 0: + cursor.execute('SELECT * FROM bounding_boxes') + for row in cursor: + boundingBoxes.setdefault(row[1], []).append([moving.Point(row[2], row[3]), moving.Point(row[4], row[5])]) + except sqlite3.OperationalError as error: + printDBError(error) return boundingBoxes ######################### @@ -624,49 +639,52 @@ connection.commit() def loadPrototypeAssignmentsFromSqlite(filename, objectType): - with sqlite3.connect(filename) as connection: - cursor = connection.cursor() - try: - tableName, objectIdColumnName = prototypeAssignmentNames(objectType) - cursor.execute('SELECT * FROM '+tableName) - prototypeAssignments = {} - for row in cursor: - p = moving.Prototype(row[1], row[2], row[3]) - if p in prototypeAssignments: - prototypeAssignments[p].append(row[0]) - else: - prototypeAssignments[p] = [row[0]] - return prototypeAssignments - except sqlite3.OperationalError as error: - printDBError(error) - + prototypeAssignments = {} + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + cursor = connection.cursor() + try: + tableName, objectIdColumnName = prototypeAssignmentNames(objectType) + cursor.execute('SELECT * FROM '+tableName) + for row in cursor: + p = moving.Prototype(row[1], row[2], row[3]) + if p in prototypeAssignments: + prototypeAssignments[p].append(row[0]) + else: + prototypeAssignments[p] = [row[0]] + return prototypeAssignments + except sqlite3.OperationalError as error: + printDBError(error) + return prototypeAssignments + def loadPrototypesFromSqlite(filename, withTrajectories = True): 'Loads prototype ids and matchings (if stored)' prototypes = [] - with sqlite3.connect(filename) as connection: - cursor = connection.cursor() - objects = [] - try: - cursor.execute('SELECT * FROM prototypes') - for row in cursor: - prototypes.append(moving.Prototype(row[0], row[1], row[2], row[3])) - if withTrajectories: - for p in prototypes: - p.setMovingObject(loadTrajectoriesFromSqlite(p.getFilename(), p.getTrajectoryType(), [p.getNum()])[0]) - # loadingInformation = {} # complicated slightly optimized - # for p in prototypes: - # dbfn = p.getFilename() - # trajType = p.getTrajectoryType() - # if (dbfn, trajType) in loadingInformation: - # loadingInformation[(dbfn, trajType)].append(p) - # else: - # loadingInformation[(dbfn, trajType)] = [p] - # for k, v in loadingInformation.iteritems(): - # objects += loadTrajectoriesFromSqlite(k[0], k[1], [p.getNum() for p in v]) - except sqlite3.OperationalError as error: - printDBError(error) - if len(set([p.getTrajectoryType() for p in prototypes])) > 1: - print('Different types of prototypes in database ({}).'.format(set([p.getTrajectoryType() for p in prototypes]))) + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + cursor = connection.cursor() + objects = [] + try: + cursor.execute('SELECT * FROM prototypes') + for row in cursor: + prototypes.append(moving.Prototype(row[0], row[1], row[2], row[3])) + if withTrajectories: + for p in prototypes: + p.setMovingObject(loadTrajectoriesFromSqlite(p.getFilename(), p.getTrajectoryType(), [p.getNum()])[0]) + # loadingInformation = {} # complicated slightly optimized + # for p in prototypes: + # dbfn = p.getFilename() + # trajType = p.getTrajectoryType() + # if (dbfn, trajType) in loadingInformation: + # loadingInformation[(dbfn, trajType)].append(p) + # else: + # loadingInformation[(dbfn, trajType)] = [p] + # for k, v in loadingInformation.iteritems(): + # objects += loadTrajectoriesFromSqlite(k[0], k[1], [p.getNum() for p in v]) + except sqlite3.OperationalError as error: + printDBError(error) + if len(set([p.getTrajectoryType() for p in prototypes])) > 1: + print('Different types of prototypes in database ({}).'.format(set([p.getTrajectoryType() for p in prototypes]))) return prototypes def savePOIsToSqlite(filename, gmm, gmmType, gmmId): @@ -703,46 +721,47 @@ from sklearn import mixture # todo if not avalaible, load data in duck-typed class with same fields from ast import literal_eval pois = [] - with sqlite3.connect(filename) as connection: - cursor = connection.cursor() - try: - cursor.execute('SELECT * from gaussians2d') - gmmId = None - gmm = [] - for row in cursor: - if gmmId is None or row[0] != gmmId: - if len(gmm) > 0: - tmp = mixture.GaussianMixture(len(gmm), covarianceType) - tmp.means_ = array([gaussian['mean'] for gaussian in gmm]) - tmp.covariances_ = array([gaussian['covar'] for gaussian in gmm]) - tmp.weights_ = array([gaussian['weight'] for gaussian in gmm]) - tmp.gmmTypes = [gaussian['type'] for gaussian in gmm] - tmp.precisions_cholesky_ = array([gaussian['precisions'] for gaussian in gmm]) - pois.append(tmp) - gaussian = {'type': row[2], - 'mean': row[3:5], - 'covar': array(literal_eval(row[5])), - 'weight': row[7], - 'precisions': array(literal_eval(row[8]))} - gmm = [gaussian] - covarianceType = row[6] - gmmId = row[0] - else: - gmm.append({'type': row[2], - 'mean': row[3:5], - 'covar': array(literal_eval(row[5])), - 'weight': row[7], - 'precisions': array(literal_eval(row[8]))}) - if len(gmm) > 0: - tmp = mixture.GaussianMixture(len(gmm), covarianceType) - tmp.means_ = array([gaussian['mean'] for gaussian in gmm]) - tmp.covariances_ = array([gaussian['covar'] for gaussian in gmm]) - tmp.weights_ = array([gaussian['weight'] for gaussian in gmm]) - tmp.gmmTypes = [gaussian['type'] for gaussian in gmm] - tmp.precisions_cholesky_ = array([gaussian['precisions'] for gaussian in gmm]) - pois.append(tmp) - except sqlite3.OperationalError as error: - printDBError(error) + if Path(filename).is_file(): + with sqlite3.connect(filename) as connection: + cursor = connection.cursor() + try: + cursor.execute('SELECT * from gaussians2d') + gmmId = None + gmm = [] + for row in cursor: + if gmmId is None or row[0] != gmmId: + if len(gmm) > 0: + tmp = mixture.GaussianMixture(len(gmm), covarianceType) + tmp.means_ = array([gaussian['mean'] for gaussian in gmm]) + tmp.covariances_ = array([gaussian['covar'] for gaussian in gmm]) + tmp.weights_ = array([gaussian['weight'] for gaussian in gmm]) + tmp.gmmTypes = [gaussian['type'] for gaussian in gmm] + tmp.precisions_cholesky_ = array([gaussian['precisions'] for gaussian in gmm]) + pois.append(tmp) + gaussian = {'type': row[2], + 'mean': row[3:5], + 'covar': array(literal_eval(row[5])), + 'weight': row[7], + 'precisions': array(literal_eval(row[8]))} + gmm = [gaussian] + covarianceType = row[6] + gmmId = row[0] + else: + gmm.append({'type': row[2], + 'mean': row[3:5], + 'covar': array(literal_eval(row[5])), + 'weight': row[7], + 'precisions': array(literal_eval(row[8]))}) + if len(gmm) > 0: + tmp = mixture.GaussianMixture(len(gmm), covarianceType) + tmp.means_ = array([gaussian['mean'] for gaussian in gmm]) + tmp.covariances_ = array([gaussian['covar'] for gaussian in gmm]) + tmp.weights_ = array([gaussian['weight'] for gaussian in gmm]) + tmp.gmmTypes = [gaussian['type'] for gaussian in gmm] + tmp.precisions_cholesky_ = array([gaussian['precisions'] for gaussian in gmm]) + pois.append(tmp) + except sqlite3.OperationalError as error: + printDBError(error) return pois ######################### @@ -1326,7 +1345,8 @@ self.stdVehicleSpeed = config.getfloat(self.sectionHeader, 'std-veh-speed') def __init__(self, filename = None): - if filename is not None and Path(filename).exists(): + self.configFilename = filename + if filename is not None and Path(filename).is_file(): self.loadConfigFile(filename) else: print('Configuration filename {} could not be loaded.'.format(filename)) @@ -1386,12 +1406,12 @@ self.videoFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'video-filename')) self.databaseFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'database-filename')) self.homographyFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'homography-filename')) - if Path(self.homographyFilename).exists(): + if Path(self.homographyFilename).is_file(): self.homography = loadtxt(self.homographyFilename) else: self.homography = None self.intrinsicCameraFilename = utils.getRelativeFilename(parentPath, config.get(self.sectionHeader, 'intrinsic-camera-filename')) - if Path(self.intrinsicCameraFilename).exists(): + if Path(self.intrinsicCameraFilename).is_file(): self.intrinsicCameraMatrix = loadtxt(self.intrinsicCameraFilename) else: self.intrinsicCameraMatrix = None @@ -1444,11 +1464,11 @@ def __init__(self, filename = None): - if filename is not None and Path(filename).exists(): + self.configFilename = filename + if filename is not None and Path(filename).is_file(): self.loadConfigFile(filename) else: print('Configuration filename {} could not be loaded.'.format(filename)) - self.configFilename = filename def processVideoArguments(args): '''Loads information from configuration file
--- a/trafficintelligence/tests/moving.txt Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/tests/moving.txt Fri Jul 20 14:03:34 2018 -0400 @@ -7,7 +7,7 @@ >>> Interval(0,1).empty() False >>> Interval(0,1) -[0, 1] +0-1 >>> Interval(0,1).length() 1.0 >>> Interval(23.2,24.9).length() @@ -15,6 +15,17 @@ >>> Interval(10,8).length() 0.0 +>>> i = Interval.parse('3-5') +>>> i.first == 3 and i.last == 5 +True +>>> type(i) +<class 'trafficintelligence.moving.Interval'> +>>> i = TimeInterval.parse('3-5') +>>> type(i) +<class 'trafficintelligence.moving.TimeInterval'> +>>> list(i) +[3, 4, 5] + >>> TimeInterval(0,1).length() 2.0 >>> TimeInterval(10,8).length() @@ -45,7 +56,7 @@ >>> TimeInterval(20,30).distance(TimeInterval(3,15)) 5 >>> TimeInterval.unionIntervals([TimeInterval(3,6), TimeInterval(8,10),TimeInterval(11,15)]) -[3, 15] +3-15 >>> Point(0,3) == Point(0,3) True
--- a/trafficintelligence/tests/storage.txt Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/tests/storage.txt Fri Jul 20 14:03:34 2018 -0400 @@ -1,4 +1,5 @@ >>> from io import StringIO +>>> from os import remove >>> from trafficintelligence.storage import * >>> from trafficintelligence.utils import openCheck, readline >>> from trafficintelligence.moving import MovingObject, Point, TimeInterval, Trajectory, prepareSplines @@ -8,11 +9,7 @@ >>> nonexistentFilename = "nonexistent" >>> loadTrajectoriesFromSqlite(nonexistentFilename, 'feature') -DB Error: no such table: positions -DB Error: no such table: velocities [] ->>> from os import remove ->>> remove(nonexistentFilename) >>> o1 = MovingObject.generate(2, Point(0.,0.), Point(1.,0.), TimeInterval(0,10)) >>> o2 = MovingObject.generate(3, Point(1.,1.), Point(-0.5,-0.2), TimeInterval(0,9)) @@ -48,6 +45,8 @@ 4 >>> objects[1].positions.length() 4 +>>> remove('test.sqlite') + >>> align1 = Trajectory.fromPointList([Point(-1, 0), Point(20, 0)]) >>> align2 = Trajectory.fromPointList([Point(-9, -3), Point(6, 3)]) >>> align1.computeCumulativeDistances()
--- a/trafficintelligence/utils.py Fri Jul 20 13:50:43 2018 -0400 +++ b/trafficintelligence/utils.py Fri Jul 20 14:03:34 2018 -0400 @@ -342,6 +342,10 @@ def timeToFrames(t, frameRate): return frameRate*(t.hour*3600+t.minute*60+t.second) +def timeModulo(t, duration): + 'returns the time modulo the duration in min' + return time(t.hour, t.minute//duration, t.second) + def sortXY(X,Y): 'returns the sorted (x, Y(x)) sorted on X' D = {} @@ -591,6 +595,19 @@ print('Unknown aggregation method: {}'.format(funcStr)) return None +def aggregationMethods(methods, centiles = None): + aggFunctions = {} + headers = [] + for method in methods: + if method == 'centile': + aggFunctions[method] = aggregationFunction(method, centiles) + for c in centiles: + headers.append('{}{}'.format(method,c)) + else: + aggFunctions[method] = aggregationFunction(method) + headers.append(method) + return aggFunctions, headers + ######################### # regression analysis using statsmodels (and pandas) #########################