comparison scripts/learn-motion-patterns.py @ 1035:933588568bec

major update to learn motion pattern, see program description
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Wed, 20 Jun 2018 16:48:20 -0400
parents 8ffb3ae9f3d2
children 5621e4ad2428
comparison
equal deleted inserted replaced
1034:4069d8545922 1035:933588568bec
1 #! /usr/bin/env python3 1 #! /usr/bin/env python3
2 2
3 import sys, argparse 3 import sys, argparse
4 4
5 import numpy as np 5 import numpy as np
6 import matplotlib.pyplot as plt
6 7
7 from trafficintelligence import ml, utils, storage, moving 8 from trafficintelligence import ml, utils, storage, moving
8 9
9 parser = argparse.ArgumentParser(description='The program learns prototypes for the motion patterns') #, epilog = '' 10 parser = argparse.ArgumentParser(description='''The program clusters trajectories, each cluster being represented by a trajectory. It can either work on the same dataset (database) or different ones, but only does learning or assignment at a time to avoid issues (the minimum cluster size argument is not used for now as it may change prototypes when assigning other trajectories)''') #, epilog = ''
10 #parser.add_argument('--cfg', dest = 'configFilename', help = 'name of the configuration file') 11 #parser.add_argument('--cfg', dest = 'configFilename', help = 'name of the configuration file')
11 parser.add_argument('-d', dest = 'databaseFilename', help = 'name of the Sqlite database file', required = True) 12 parser.add_argument('-d', dest = 'databaseFilename', help = 'name of the Sqlite database file', required = True)
12 parser.add_argument('-o', dest = 'outputPrototypeDatabaseFilename', help = 'name of the Sqlite database file to save prototypes') 13 parser.add_argument('-o', dest = 'outputPrototypeDatabaseFilename', help = 'name of the Sqlite database file to save prototypes')
13 parser.add_argument('-i', dest = 'inputPrototypeDatabaseFilename', help = 'name of the Sqlite database file for prototypes to start the algorithm with') 14 parser.add_argument('-i', dest = 'inputPrototypeDatabaseFilename', help = 'name of the Sqlite database file for prototypes to start the algorithm with')
14 parser.add_argument('-t', dest = 'trajectoryType', help = 'type of trajectories to learn from', choices = ['objectfeatures', 'feature', 'object'], default = 'objectfeatures') 15 parser.add_argument('-t', dest = 'trajectoryType', help = 'type of trajectories to learn from', choices = ['objectfeature', 'feature', 'object'], default = 'objectfeatures')
15 parser.add_argument('--max-nobjectfeatures', dest = 'maxNObjectFeatures', help = 'maximum number of features per object to load', type = int, default = 1) 16 parser.add_argument('--max-nobjectfeatures', dest = 'maxNObjectFeatures', help = 'maximum number of features per object to load', type = int, default = 1)
16 parser.add_argument('-n', dest = 'nTrajectories', help = 'number of the object or feature trajectories to load', type = int, default = None) 17 parser.add_argument('-n', dest = 'nTrajectories', help = 'number of the object or feature trajectories to load', type = int, default = None)
17 parser.add_argument('-e', dest = 'epsilon', help = 'distance for the similarity of trajectory points', type = float, required = True) 18 parser.add_argument('-e', dest = 'epsilon', help = 'distance for the similarity of trajectory points', type = float, required = True)
18 parser.add_argument('--metric', dest = 'metric', help = 'metric for the similarity of trajectory points', default = 'cityblock') # default is manhattan distance 19 parser.add_argument('--metric', dest = 'metric', help = 'metric for the similarity of trajectory points', default = 'cityblock') # default is manhattan distance
19 parser.add_argument('-s', dest = 'minSimilarity', help = 'minimum similarity to put a trajectory in a cluster', type = float, required = True) 20 parser.add_argument('-s', dest = 'minSimilarity', help = 'minimum similarity to put a trajectory in a cluster', type = float, required = True)
22 parser.add_argument('--optimize', dest = 'optimizeCentroid', help = 'recompute centroid at each assignment', action = 'store_true') 23 parser.add_argument('--optimize', dest = 'optimizeCentroid', help = 'recompute centroid at each assignment', action = 'store_true')
23 parser.add_argument('--random', dest = 'randomInitialization', help = 'random initialization of clustering algorithm', action = 'store_true') 24 parser.add_argument('--random', dest = 'randomInitialization', help = 'random initialization of clustering algorithm', action = 'store_true')
24 parser.add_argument('--subsample', dest = 'positionSubsamplingRate', help = 'rate of position subsampling (1 every n positions)', type = int) 25 parser.add_argument('--subsample', dest = 'positionSubsamplingRate', help = 'rate of position subsampling (1 every n positions)', type = int)
25 parser.add_argument('--display', dest = 'display', help = 'display trajectories', action = 'store_true') 26 parser.add_argument('--display', dest = 'display', help = 'display trajectories', action = 'store_true')
26 parser.add_argument('--save-similarities', dest = 'saveSimilarities', help = 'save computed similarities (in addition to prototypes)', action = 'store_true') 27 parser.add_argument('--save-similarities', dest = 'saveSimilarities', help = 'save computed similarities (in addition to prototypes)', action = 'store_true')
27 parser.add_argument('--save-matches', dest = 'saveMatches', help = 'saves the assignments of the objects (not for features) to the prototypes', action = 'store_true') 28 parser.add_argument('--save-assignments', dest = 'saveAssignments', help = 'saves the assignments of the objects to the prototypes', action = 'store_true')
28 parser.add_argument('--assign', dest = 'assign', help = 'assigns the objects to the prototypes and saves the assignments', action = 'store_true') 29 parser.add_argument('--assign', dest = 'assign', help = 'assigns the objects to the prototypes and saves the assignments', action = 'store_true')
29 30
30 args = parser.parse_args() 31 args = parser.parse_args()
31 32
32 # use cases 33 # use cases
33 # 1. learn proto from one file, save in same or another 34 # 1. learn proto from one file, save in same or another
34 # 2. load proto, load objects, update proto, save proto 35 # 2. load proto, load objects (from same or other db), update proto matchings, save proto
35 # 3. assign objects from one db to proto 36 # TODO 3. on same dataset, learn and assign trajectories (could be done with min cluster size)
36 # 4. load objects from several files, save in another -> see metadata: site with view and times 37 # TODO? 4. when assigning, allow min cluster size only to avoid assigning to small clusters (but prototypes are not removed even if in small clusters, can be done after assignment with nmatchings)
37 # 5. keep prototypes, with positions/velocities, in separate db (keep link to original data through filename, type and index)
38 38
39 # TODO add possibility to cluster with velocities 39 # TODO add possibility to cluster with velocities
40 # TODO add possibilite to load all trajectories and use minclustersize 40 # TODO add possibility to load all trajectories and use minclustersize
41 # save the objects that match the prototypes
42 # write an assignment function for objects
43 41
44 # load trajectories to cluster or assign 42 # load trajectories to cluster or assign
45 if args.trajectoryType == 'objectfeatures': 43 if args.trajectoryType == 'objectfeature':
46 trajectoryType = 'feature' 44 trajectoryType = 'feature'
47 objectFeatureNumbers = storage.loadObjectFeatureFrameNumbers(args.databaseFilename, objectNumbers = args.nTrajectories) 45 objectFeatureNumbers = storage.loadObjectFeatureFrameNumbers(args.databaseFilename, objectNumbers = args.nTrajectories)
48 featureNumbers = [] 46 featureNumbers = []
49 for numbers in objectFeatureNumbers.values(): 47 for numbers in objectFeatureNumbers.values():
50 featureNumbers += numbers[:min(len(numbers), args.maxNObjectFeatures)] 48 featureNumbers += numbers[:min(len(numbers), args.maxNObjectFeatures)]
86 # prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc, args.minClusterSize) 84 # prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc, args.minClusterSize)
87 # else: 85 # else:
88 # print('Not assigning with non-zero minimum cluster size and initial prototypes (would remove initial prototypes based on other trajectories') 86 # print('Not assigning with non-zero minimum cluster size and initial prototypes (would remove initial prototypes based on other trajectories')
89 # else: 87 # else:
90 # prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc) 88 # prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc)
91 prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc) 89 assignedPrototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc)
92 clusterSizes = ml.computeClusterSizes(labels, prototypeIndices, -1)
93 print(clusterSizes)
94 90
95 if args.learn and not args.assign: 91 if args.learn and not args.assign:
96 prototypes = [] 92 prototypes = []
97 for i in prototypeIndices: 93 for i in prototypeIndices:
98 if i<len(initialPrototypes): 94 if i<len(initialPrototypes):
105 else: 101 else:
106 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename 102 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename
107 if args.inputPrototypeDatabaseFilename == args.outputPrototypeDatabaseFilename: 103 if args.inputPrototypeDatabaseFilename == args.outputPrototypeDatabaseFilename:
108 storage.deleteFromSqlite(args.outputPrototypeDatabaseFilename, 'prototype') 104 storage.deleteFromSqlite(args.outputPrototypeDatabaseFilename, 'prototype')
109 storage.savePrototypesToSqlite(outputPrototypeDatabaseFilename, prototypes) 105 storage.savePrototypesToSqlite(outputPrototypeDatabaseFilename, prototypes)
106 if args.display:
107 plt.figure()
108 for p in prototypes:
109 p.getMovingObject().plot()
110 plt.axis('equal')
111 plt.show()
110 112
111 if not args.learn and args.assign: # no new prototypes # not save assignments of past prototypes if removes with minClusterSize 113 if not args.learn and args.assign: # no modification to prototypes, can work with initialPrototypes
112 prototypes = [] 114 clusterSizes = ml.computeClusterSizes(labels, prototypeIndices, -1)
113 for i in prototypeIndices: 115 for i in prototypeIndices:
114 nMatchings = clusterSizes[i]-1 116 nMatchings = clusterSizes[i]-1
115 if initialPrototypes[i].nMatchings is None: 117 if initialPrototypes[i].nMatchings is None:
116 initialPrototypes[i].nMatchings = nMatchings 118 initialPrototypes[i].nMatchings = nMatchings
117 else: 119 else:
118 initialPrototypes[i].nMatchings += nMatchings 120 initialPrototypes[i].nMatchings += nMatchings
119 prototypes.append(initialPrototypes[i])
120 if args.outputPrototypeDatabaseFilename is None: 121 if args.outputPrototypeDatabaseFilename is None:
121 outputPrototypeDatabaseFilename = args.databaseFilename 122 outputPrototypeDatabaseFilename = args.databaseFilename
122 else: 123 else:
123 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename 124 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename
124 storage.setPrototypeMatchingsInSqlite(outputPrototypeDatabaseFilename, prototypes) 125 storage.setPrototypeMatchingsInSqlite(outputPrototypeDatabaseFilename, initialPrototypes)
125 126 if args.saveAssignments:
126 labelsToProtoIndices = {protoId: i for i, protoId in enumerate(prototypeIndices)} 127 if args.trajectoryType == 'objectfeature': # consider that the object is assigned through its longest features
127 if args.saveMatches: 128 objectNumbers = []
128 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, objects, trajectoryType, [labelsToProtoIndices[l] for l in labels], prototypes) 129 objectLabels = []
130 for objNum, objFeatureNumbers in objectFeatureNumbers.items():
131 objLabels = []
132 for i, o in enumerate(objects):
133 if o.getNum() in objFeatureNumbers:
134 objLabels.append(labels[i+len(initialPrototypes)])
135 objectLabels.append(utils.mostCommon(objLabels))
136 objectNumbers.append(objNum)
137 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, objectNumbers, 'object', objectLabels, initialPrototypes)
138 else:
139 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, [obj.getNum() for obj in objects], trajectoryType, labels[len(initialPrototypes):], initialPrototypes)
140 if args.display:
141 plt.figure()
142 for i,o in enumerate(objects):
143 if labels[i+len(initialPrototypes)] < 0:
144 o.plot('kx-')
145 else:
146 o.plot(utils.colors[labels[i+len(initialPrototypes)]])
147 for i,p in enumerate(initialPrototypes):
148 p.getMovingObject().plot(utils.colors[i]+'o')
149 plt.axis('equal')
150 plt.show()
129 151
130 if (args.learn or args.assign) and args.saveSimilarities: 152 if (args.learn or args.assign) and args.saveSimilarities:
131 np.savetxt(utils.removeExtension(args.databaseFilename)+'-prototype-similarities.txt.gz', similarities, '%.4f') 153 np.savetxt(utils.removeExtension(args.databaseFilename)+'-prototype-similarities.txt.gz', similarities, '%.4f')
132 154
133 if args.display and args.assign:
134 from matplotlib.pyplot import figure, show, axis
135 figure()
136 for i,o in enumerate(objects):
137 if i not in prototypeIndices:
138 if labels[i] < 0:
139 o.plot('kx')
140 else:
141 o.plot(utils.colors[labels[i]])
142 for i in prototypeIndices:
143 objects[i].plot(utils.colors[i]+'o')
144 axis('equal')
145 show()