comparison scripts/learn-motion-patterns.py @ 1044:75a6ad604cc5

work on motion patterns
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Thu, 05 Jul 2018 17:06:40 -0400
parents b735895c8815
children f2ba9858e6c6
comparison
equal deleted inserted replaced
1043:b735895c8815 1044:75a6ad604cc5
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 import matplotlib.pyplot as plt
7 7
8 from trafficintelligence import ml, utils, storage, moving 8 from trafficintelligence import ml, utils, storage, moving, processing
9 9
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 = 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''') #, epilog = ''
11 #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')
12 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)
13 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')
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('-i', dest = 'inputPrototypeDatabaseFilename', help = 'name of the Sqlite database file for prototypes to start the algorithm with')
15 parser.add_argument('-t', dest = 'trajectoryType', help = 'type of trajectories to learn from', choices = ['objectfeature', 'feature', 'object'], default = 'objectfeature') 15 parser.add_argument('-t', dest = 'trajectoryType', help = 'type of trajectories to process', choices = ['feature', 'object'], default = 'feature')
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('--nfeatures-per-object', dest = 'nLongestFeaturesPerObject', help = 'maximum number of features per object to load', type = int)
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('-n', dest = 'nTrajectories', help = 'number of the object or feature trajectories to load', type = int, default = None)
18 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)
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('--metric', dest = 'metric', help = 'metric for the similarity of trajectory points', default = 'cityblock') # default is manhattan distance
20 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)
21 parser.add_argument('-c', dest = 'minClusterSize', help = 'minimum cluster size', type = int, default = 0) 21 #parser.add_argument('-c', dest = 'minClusterSize', help = 'minimum cluster size', type = int, default = 0)
22 parser.add_argument('--learn', dest = 'learn', help = 'learn', action = 'store_true') 22 parser.add_argument('--learn', dest = 'learn', help = 'learn', action = 'store_true')
23 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')
24 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')
25 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)
26 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')
38 # 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) 38 # 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)
39 39
40 # TODO add possibility to cluster with velocities 40 # TODO add possibility to cluster with velocities
41 # TODO add possibility to load all trajectories and use minclustersize 41 # TODO add possibility to load all trajectories and use minclustersize
42 42
43 # load trajectories to cluster or assign 43 if args.learn and args.assign:
44 objects = storage.loadTrajectoriesFromSqlite(args.databaseFilename, args.trajectoryType, args.nTrajectories, timeStep = args.positionSubsamplingRate) 44 print('Cannot learn and assign simultaneously')
45 trajectories = [o.getPositions().asArray().T for o in objects] 45 sys.exit(0)
46
47 objects = storage.loadTrajectoriesFromSqlite(args.databaseFilename, args.trajectoryType, args.nTrajectories, timeStep = args.positionSubsamplingRate, nLongestFeaturesPerObject = args.nLongestFeaturesPerObject)
48 if args.trajectoryType == 'object' and args.nLongestFeaturesPerObject is not None:
49 objectsWithFeatures = objects
50 objects = [f for o in objectsWithFeatures for f in o.getFeatures()]
51 prototypeType = 'feature'
52 else:
53 prototypeType = args.trajectoryType
46 54
47 # load initial prototypes, if any 55 # load initial prototypes, if any
48 if args.inputPrototypeDatabaseFilename is not None: 56 if args.inputPrototypeDatabaseFilename is not None:
49 initialPrototypes = storage.loadPrototypesFromSqlite(args.inputPrototypeDatabaseFilename, True) 57 initialPrototypes = storage.loadPrototypesFromSqlite(args.inputPrototypeDatabaseFilename, True)
50 trajectories = [p.getMovingObject().getPositions().asArray().T for p in initialPrototypes]+trajectories
51 if len(initialPrototypes) > 0:
52 initialPrototypeIndices = list(range(len(initialPrototypes)))
53 else:
54 initialPrototypeIndices = None
55 else: 58 else:
56 initialPrototypes = [] 59 initialPrototypes = []
57 initialPrototypeIndices = None
58 60
59 lcss = utils.LCSS(metric = args.metric, epsilon = args.epsilon) 61 lcss = utils.LCSS(metric = args.metric, epsilon = args.epsilon)
62 similarityFunc = lambda x,y : lcss.computeNormalized(x, y)
63 nTrajectories = len(initialPrototypes)+len(objects)
60 if args.similaritiesFilename is not None: 64 if args.similaritiesFilename is not None:
61 similarities = np.loadtxt(args.similaritiesFilename) 65 similarities = np.loadtxt(args.similaritiesFilename)
62 if args.similaritiesFilename is None or similarities.shape[0] != len(trajectories) or similarities.shape[1] != len(trajectories): 66 if args.similaritiesFilename is None or similarities.shape[0] != nTrajectories or similarities.shape[1] != nTrajectories:
63 similarities = -np.ones((len(trajectories), len(trajectories))) 67 similarities = -np.ones((nTrajectories, nTrajectories))
64 similarityFunc = lambda x,y : lcss.computeNormalized(x, y)
65 # the next line can be called again without reinitializing similarities
66 if args.learn:
67 prototypeIndices = ml.prototypeCluster(trajectories, similarities, args.minSimilarity, similarityFunc, args.optimizeCentroid, args.randomInitialization, initialPrototypeIndices)
68 else:
69 prototypeIndices = initialPrototypeIndices
70 68
71 if args.assign: # TODO don't touch initial prototypes if not from same db as trajectories 69 prototypeIndices, labels = processing.learnAssignMotionPatterns(args.learn, args.assign, objects, similarities, args.minSimilarity, similarityFunc, 0, args.optimizeCentroid, args.randomInitialization, False, initialPrototypes)
72 #if not args.learn and args.minClusterSize >= 1: # allow only
73 # print('Warning: you did not learn the prototypes and you are using minimum cluster size of {}, which may lead to removing prototypes and assigning them to others'.format(args.minClusterSize))
74 # if args.minClusterSize >= 1:
75 # if initialPrototypeIndices is None:
76 # prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc, args.minClusterSize)
77 # else:
78 # print('Not assigning with non-zero minimum cluster size and initial prototypes (would remove initial prototypes based on other trajectories')
79 # else:
80 # prototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc)
81 assignedPrototypeIndices, labels = ml.assignToPrototypeClusters(trajectories, prototypeIndices, similarities, args.minSimilarity, similarityFunc)
82 70
83 if args.learn and not args.assign: 71 if args.learn:# and not args.assign:
84 prototypes = [] 72 prototypes = []
85 if args.trajectoryType == 'objectfeature':
86 trajectoryType = 'feature'
87 else:
88 trajectoryType = args.trajectoryType
89 for i in prototypeIndices: 73 for i in prototypeIndices:
90 if i<len(initialPrototypes): 74 if i<len(initialPrototypes):
91 prototypes.append(initialPrototypes[i]) 75 prototypes.append(initialPrototypes[i])
92 else: 76 else:
93 prototypes.append(moving.Prototype(args.databaseFilename, objects[i-len(initialPrototypes)].getNum(), trajectoryType)) 77 prototypes.append(moving.Prototype(args.databaseFilename, objects[i-len(initialPrototypes)].getNum(), prototypeType))
94 78
95 if args.outputPrototypeDatabaseFilename is None: 79 if args.outputPrototypeDatabaseFilename is None:
96 outputPrototypeDatabaseFilename = args.databaseFilename 80 outputPrototypeDatabaseFilename = args.databaseFilename
97 else: 81 else:
98 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename 82 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename
104 for p in prototypes: 88 for p in prototypes:
105 p.getMovingObject().plot() 89 p.getMovingObject().plot()
106 plt.axis('equal') 90 plt.axis('equal')
107 plt.show() 91 plt.show()
108 92
109 if not args.learn and args.assign: # no modification to prototypes, can work with initialPrototypes 93 if args.assign: # not args.learn and no modification to prototypes, can work with initialPrototypes
110 clusterSizes = ml.computeClusterSizes(labels, prototypeIndices, -1) 94 clusterSizes = ml.computeClusterSizes(labels, prototypeIndices, -1)
111 for i in prototypeIndices: 95 for i in prototypeIndices:
112 nMatchings = clusterSizes[i]-1 96 nMatchings = clusterSizes[i]-1 # external prototypes
113 if initialPrototypes[i].nMatchings is None: 97 if initialPrototypes[i].nMatchings is None:
114 initialPrototypes[i].nMatchings = nMatchings 98 initialPrototypes[i].nMatchings = nMatchings
115 else: 99 else:
116 initialPrototypes[i].nMatchings += nMatchings 100 initialPrototypes[i].nMatchings += nMatchings
117 if args.outputPrototypeDatabaseFilename is None: 101 if args.outputPrototypeDatabaseFilename is None:
118 outputPrototypeDatabaseFilename = args.databaseFilename 102 outputPrototypeDatabaseFilename = args.databaseFilename
119 else: 103 else:
120 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename 104 outputPrototypeDatabaseFilename = args.outputPrototypeDatabaseFilename
121 storage.setPrototypeMatchingsInSqlite(outputPrototypeDatabaseFilename, initialPrototypes) 105 storage.setPrototypeMatchingsInSqlite(outputPrototypeDatabaseFilename, initialPrototypes)
122 if args.saveAssignments: 106 if args.saveAssignments:
123 if args.trajectoryType == 'objectfeature': # consider that the object is assigned through its longest features 107 if args.trajectoryType == 'object' and args.nLongestFeaturesPerObject is not None:
108 # consider that the object is assigned through its longest features
109 # issues are inconsistencies in the number of matchings per prototype and display (will display features, not objects)
124 objectNumbers = [] 110 objectNumbers = []
125 objectLabels = [] 111 objectLabels = []
126 for objNum, objFeatureNumbers in objectFeatureNumbers.items(): 112 i = 0
113 for obj in objectsWithFeatures:
127 objLabels = [] 114 objLabels = []
128 for i, o in enumerate(objects): 115 for f in obj.getFeatures():
129 if o.getNum() in objFeatureNumbers: 116 if f == objects[i]:
130 objLabels.append(labels[i+len(initialPrototypes)]) 117 objLabels.append(labels[i+len(initialPrototypes)])
118 i += 1
119 else:
120 print('Issue with obj {} and feature {} (trajectory {})'.format(obj.getNum(), f.getNum(), i))
131 objectLabels.append(utils.mostCommon(objLabels)) 121 objectLabels.append(utils.mostCommon(objLabels))
132 objectNumbers.append(objNum) 122 objectNumbers.append(obj.getNum())
133 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, objectNumbers, 'object', objectLabels, initialPrototypes) 123 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, objectNumbers, 'object', objectLabels, initialPrototypes)
134 else: 124 else:
135 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, [obj.getNum() for obj in objects], args.trajectoryType, labels[len(initialPrototypes):], initialPrototypes) 125 storage.savePrototypeAssignmentsToSqlite(args.databaseFilename, [obj.getNum() for obj in objects], args.trajectoryType, labels[len(initialPrototypes):], initialPrototypes)
136 if args.display: 126 if args.display:
137 plt.figure() 127 plt.figure()