changeset 708:a37c565f4b68

merged dev
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Wed, 22 Jul 2015 14:17:44 -0400
parents 7efa36b9bcfd (current diff) e395bffe1412 (diff)
children 29daabe094fe 70a3cdf0dbb3
files scripts/classify-objects.py
diffstat 17 files changed, 482 insertions(+), 392 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile	Wed Jul 22 14:17:19 2015 -0400
+++ b/Makefile	Wed Jul 22 14:17:44 2015 -0400
@@ -1,3 +1,4 @@
+INSTALL_DIR = /usr/local/bin
 
 cexe:
 	@cd c && make feature-based-tracking
@@ -17,9 +18,9 @@
 	@cp bin/feature-based-tracking /usr/local/bin
 	@echo "========================================="
 	@echo "Copying Python scripts"
-	@cp scripts/* /usr/local/bin
+	@cp scripts/* $(INSTALL_DIR)
 
 uninstall:
 	@echo "Uninstalling for Linux"
-	rm /usr/local/bin/feature-based-tracking 
-	@cd scripts && ./uninstall-scripts.sh
\ No newline at end of file
+	rm $(INSTALL_DIR)/feature-based-tracking 
+	@cd scripts && ./uninstall-scripts.sh $(INSTALL_DIR)
--- a/c/utils.cpp	Wed Jul 22 14:17:19 2015 -0400
+++ b/c/utils.cpp	Wed Jul 22 14:17:44 2015 -0400
@@ -4,7 +4,6 @@
 
 #include <iostream>
 #include <fstream>
-#include <sstream>
 
 using namespace std;
 
--- a/include/utils.hpp	Wed Jul 22 14:17:19 2015 -0400
+++ b/include/utils.hpp	Wed Jul 22 14:17:44 2015 -0400
@@ -1,6 +1,7 @@
 #ifndef UTILS_HPP
 #define UTILS_HPP
 
+#include <sstream>
 #include <iosfwd>
 #include <string>
 #include <vector>
@@ -73,7 +74,8 @@
 template<typename T>
 bool fromString(T & result, const std::string & s) {
   std::istringstream iss(s);
-  return iss >> result != 0;
+  iss >> result;
+  return iss.good() || iss.eof();
 }
 
 #endif
--- a/python/cvutils.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/cvutils.py	Wed Jul 22 14:17:44 2015 -0400
@@ -17,7 +17,7 @@
     skimageAvailable = False
     
 from sys import stdout
-from numpy import dot, array, append
+from numpy import dot, array, append, float32
 
 #import aggdraw # agg on top of PIL (antialiased drawing)
 
@@ -126,7 +126,8 @@
     def playVideo(filename, firstFrameNum = 0, frameRate = -1, interactive = False, printFrames = True, text = None, rescale = 1., step = 1):
         '''Plays the video'''
         windowName = 'frame'
-        cv2.namedWindow(windowName, cv2.WINDOW_NORMAL)
+        if rescale == 1.:
+            cv2.namedWindow(windowName, cv2.WINDOW_NORMAL)
         wait = 5
         if frameRate > 0:
             wait = int(round(1000./frameRate))
@@ -200,7 +201,10 @@
         images = []
         capture = cv2.VideoCapture(videoFilename)
         if capture.isOpened():
-            nDigits = int(floor(log10(capture.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT))))+1
+            rawCount = capture.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT)
+            if rawCount < 0:
+                rawCount = firstFrameNum+nFrames+1
+            nDigits = int(floor(log10(rawCount)))+1
             ret = False
             capture.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, firstFrameNum)
             imgNum = 0
@@ -232,7 +236,7 @@
             print('Video capture for {} failed'.format(videoFilename))
             return None
 
-    def imageBox(img, obj, frameNum, homography, width, height, px = 0.2, py = 0.2, pixelThreshold = 800):
+    def imageBox(img, obj, frameNum, homography, width, height, px = 0.2, py = 0.2, minNPixels = 800):
         'Computes the bounding box of object at frameNum'
         x = []
         y = []
@@ -253,10 +257,10 @@
         yCropMax = int(min(height - 1, .5 * (ymin + ymax + a)))
         xCropMin = int(max(0, .5 * (xmin + xmax - a)))
         xCropMax = int(min(width - 1, .5 * (xmin + xmax + a)))
-        if yCropMax != yCropMin and xCropMax != xCropMin and (yCropMax - yCropMin) * (xCropMax - xCropMin) > pixelThreshold:
+        if yCropMax != yCropMin and xCropMax != xCropMin and (yCropMax - yCropMin) * (xCropMax - xCropMin) > minNPixels:
             croppedImg = img[yCropMin : yCropMax, xCropMin : xCropMax]
         else:
-            croppedImg = []
+            croppedImg = None
         return croppedImg, yCropMin, yCropMax, xCropMin, xCropMax
 
 
@@ -270,7 +274,8 @@
         height = int(capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))
 
         windowName = 'frame'
-        #cv2.namedWindow(windowName, cv2.WINDOW_NORMAL)
+        if rescale == 1.:
+            cv2.namedWindow(windowName, cv2.WINDOW_NORMAL)
 
         if undistort: # setup undistortion
             [map1, map2] = computeUndistortMaps(width, height, undistortedImageMultiplication, intrinsicCameraMatrix, distortionCoefficients)
@@ -538,10 +543,10 @@
             return None
 
 if skimageAvailable:
+    from skimage.feature import hog
+    from skimage import color, transform
+    
     def HOG(image, rescaleSize = (64, 64), orientations=9, pixelsPerCell=(8, 8), cellsPerBlock=(2, 2), visualize=False, normalize=False):
-        from skimage.feature import hog
-        from skimage import color, transform
-
         bwImg = color.rgb2gray(image)
         inputImg = transform.resize(bwImg, rescaleSize)
         features = hog(inputImg, orientations, pixelsPerCell, cellsPerBlock, visualize, normalize)
@@ -551,14 +556,13 @@
             features = features[0]
             figure()
             subplot(1,2,1)
-            imshow(img)
+            imshow(inputImg)
             subplot(1,2,2)
             imshow(hogViz)
-        return features
+        return float32(features)
 
     def createHOGTrainingSet(imageDirectory, classLabel, rescaleSize = (64, 64), orientations=9, pixelsPerCell=(8, 8), cellsPerBlock=(2, 2), visualize=False, normalize=False):
         from os import listdir
-        from numpy import float32
         from matplotlib.pyplot import imread
 
         inputData = []
--- a/python/events.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/events.py	Wed Jul 22 14:17:44 2015 -0400
@@ -6,7 +6,6 @@
 from base import VideoFilenameAddable
 
 import numpy as np
-from numpy import arccos
 
 import multiprocessing
 import itertools
@@ -90,6 +89,8 @@
                       '',
                       '']
 
+    timeIndicators = ['Time to Collision', 'predicted Post Encroachment Time']
+
     def __init__(self, num = None, timeInterval = None, roaduserNum1 = None, roaduserNum2 = None, roadUser1 = None, roadUser2 = None, categoryNum = None):
         moving.STObject.__init__(self, num, timeInterval)
         if timeInterval is None and roadUser1 is not None and roadUser2 is not None:
@@ -113,20 +114,23 @@
         return self.roadUserNumbers
 
     def setRoadUsers(self, objects):
-        nums = list(self.getRoadUserNumbers())
-        if objects[nums[0]].getNum() == nums[0]:
+        nums = sorted(list(self.getRoadUserNumbers()))
+        if nums[0]<len(objects) and objects[nums[0]].getNum() == nums[0]:
             self.roadUser1 = objects[nums[0]]
-        if objects[nums[1]].getNum() == nums[1]:
+        if nums[1]<len(objects) and objects[nums[1]].getNum() == nums[1]:
             self.roadUser2 = objects[nums[1]]
 
-        i = 0
-        while i < len(objects) and self.roadUser2 is None:
-            if objects[i].getNum() in nums:
-                if self.roadUser1 is None:
-                    self.roadUser1 = objects[i]
-                else:
-                    self.roadUser2 = objects[i]
-            i += 1
+        if self.roadUser1 is None or self.roadUser2 is None:
+            self.roadUser1 = None
+            self.roadUser2 = None
+            i = 0
+            while i < len(objects) and self.roadUser2 is None:
+                if objects[i].getNum() in nums:
+                    if self.roadUser1 is None:
+                        self.roadUser1 = objects[i]
+                    else:
+                        self.roadUser2 = objects[i]
+                i += 1
 
     def getIndicator(self, indicatorName):
         return self.indicators.get(indicatorName, None)
@@ -177,14 +181,14 @@
             v1 = self.roadUser1.getVelocityAtInstant(instant)
             v2 = self.roadUser2.getVelocityAtInstant(instant)
             deltav = v2-v1
-            velocityAngles[instant] = arccos(moving.Point.dot(v1, v2)/(v1.norm2()*v2.norm2()))
+            velocityAngles[instant] = np.arccos(moving.Point.dot(v1, v2)/(v1.norm2()*v2.norm2()))
             collisionCourseDotProducts[instant] = moving.Point.dot(deltap, deltav)
             distances[instant] = deltap.norm2()
             speedDifferentials[instant] = deltav.norm2()
             if collisionCourseDotProducts[instant] > 0:
                 interactionInstants.append(instant)
             if distances[instant] != 0 and speedDifferentials[instant] != 0:
-                collisionCourseAngles[instant] = arccos(collisionCourseDotProducts[instant]/(distances[instant]*speedDifferentials[instant]))
+                collisionCourseAngles[instant] = np.arccos(collisionCourseDotProducts[instant]/(distances[instant]*speedDifferentials[instant]))
 
         if len(interactionInstants) >= 2:
             self.interactionInterval = moving.TimeInterval(interactionInstants[0], interactionInstants[-1])
@@ -192,16 +196,16 @@
             self.interactionInterval = moving.TimeInterval()
         self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[0], collisionCourseDotProducts))
         self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[1], collisionCourseAngles))
-        self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[2], distances))
+        self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[2], distances, mostSevereIsMax = False))
         self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[4], velocityAngles))
         self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[5], speedDifferentials))
 
         # if we have features, compute other indicators
         if self.roadUser1.hasFeatures() and self.roadUser2.hasFeatures():
-            minDistance={}
+            minDistances={}
             for instant in self.timeInterval:
-                minDistance[instant] = moving.MovingObject.minDistance(self.roadUser1, self.roadUser2, instant)
-            self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[3], minDistance))
+                minDistances[instant] = moving.MovingObject.minDistance(self.roadUser1, self.roadUser2, instant)
+            self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[3], minDistances, mostSevereIsMax = False))
 
     def computeCrossingsCollisions(self, predictionParameters, collisionDistanceThreshold, timeHorizon, computeCZ = False, debug = False, timeInterval = None, nProcesses = 1, usePrototypes=False, route1= (-1,-1), route2=(-1,-1), prototypes={}, secondStepPrototypes={}, nMatching={}, objects=[], noiseEntryNums=[], noiseExitNums=[], minSimilarity=0.1, mostMatched=None, useDestination=True, useSpeedPrototype=True, acceptPartialLength=30, step=1):
         '''Computes all crossing and collision points at each common instant for two road users. '''
@@ -217,11 +221,12 @@
         self.collisionPoints, crossingZones = predictionParameters.computeCrossingsCollisions(self.roadUser1, self.roadUser2, collisionDistanceThreshold, timeHorizon, computeCZ, debug, commonTimeInterval, nProcesses,usePrototypes,route1,route2,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype,acceptPartialLength, step)
         for i, cp in self.collisionPoints.iteritems():
             TTCs[i] = prediction.SafetyPoint.computeExpectedIndicator(cp)
-        self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[7], TTCs, mostSevereIsMax=False))
+        if len(TTCs) > 0:
+            self.addIndicator(indicators.SeverityIndicator(Interaction.indicatorNames[7], TTCs, mostSevereIsMax=False))
         
         # crossing zones and pPET
         if computeCZ:
-            self.crossingZones[predictionParameters.name] = crossingZones
+            self.crossingZones = crossingZones
             pPETs = {}
             for i, cz in self.crossingZones.iteritems():
                 pPETs[i] = prediction.SafetyPoint.computeExpectedIndicator(cz)
@@ -242,18 +247,11 @@
         else:
             return None
 
-    def getCrossingZones(self, predictionMethodName):
-        if self.crossingZones is not None:
-            return self.crossingZones[predictionMethodName]
-        else:
-            return None
+    def getCollisionPoints(self):
+        return self.collisionPoints
 
-    def getCollisionPoints(self, predictionMethodName):
-        if self.collisionPoints is not None:
-            return self.collisionPoints[predictionMethodName]
-        else:
-            return None
-
+    def getCrossingZones(self):
+        return self.crossingZones
 
 def createInteractions(objects, _others = None):
     '''Create all interactions of two co-existing road users'''
@@ -282,22 +280,19 @@
     else:
         return None
 
-def aggregateSafetyPoints(interactions, predictionMethodName = None, pointType = 'collision'):
+def aggregateSafetyPoints(interactions, pointType = 'collision'):
     '''Put all collision points or crossing zones in a list for display'''
-    if predictionMethodName is None and len(interactions)>0:
-        predictionMethodName = interactions[0].collisionPoints.keys()[0]
-
     allPoints = []
     if pointType == 'collision':
         for i in interactions:
-            for points in i.collisionPoints[predictionMethodName].values():
+            for points in i.collisionPoints.values():
                 allPoints += points
     elif pointType == 'crossing':
         for i in interactions:
-            for points in i.crossingZones[predictionMethodName].values():
+            for points in i.crossingZones.values():
                 allPoints += points
     else:
-        print('unknown type of point '+pointType)
+        print('unknown type of point: '+pointType)
     return allPoints
 
 def prototypeCluster(interactions, similarityMatrix, alignmentMatrix, indicatorName, minSimilarity):
@@ -343,130 +338,6 @@
     Non-prototype interactions will be assigned to an existing prototype if all indicators are similar enough'''
     pass
 
-# TODO:
-#http://stackoverflow.com/questions/3288595/multiprocessing-using-pool-map-on-a-function-defined-in-a-class
-#http://www.rueckstiess.net/research/snippets/show/ca1d7d90
-def calculateIndicatorPipe(pairs, predParam, timeHorizon=75,collisionDistanceThreshold=1.8):  
-    collisionPoints, crossingZones = prediction.computeCrossingsCollisions(pairs.roadUser1, pairs.roadUser2, predParam, collisionDistanceThreshold, timeHorizon)      
-    #print pairs.num    
-    # Ignore empty collision points
-    empty = 1
-    for i in collisionPoints:
-        if(collisionPoints[i] != []):
-            empty = 0
-    if(empty == 1):
-        pairs.hasCP = 0
-    else:
-        pairs.hasCP = 1
-    pairs.CP = collisionPoints
-    
-    # Ignore empty crossing zones
-    empty = 1
-    for i in crossingZones:
-        if(crossingZones[i] != []):
-            empty = 0
-    if(empty == 1):
-        pairs.hasCZ = 0
-    else:
-        pairs.hasCZ = 1
-    pairs.CZ = crossingZones
-    return pairs
-
-def calculateIndicatorPipe_star(a_b):
-    """Convert `f([1,2])` to `f(1,2)` call."""
-    return calculateIndicatorPipe(*a_b)
-
-class VehPairs():
-    '''Create a veh-pairs object from objects list'''
-    def __init__(self,objects):
-        self.pairs = createInteractions(objects)
-        self.interactionCount = 0
-        self.CPcount = 0
-        self.CZcount = 0
-    
-    # Process indicator calculation with support for multi-threading
-    def calculateIndicators(self,predParam,threads=1,timeHorizon=75,collisionDistanceThreshold=1.8):       
-        if(threads > 1):
-            pool = multiprocessing.Pool(threads)
-            self.pairs = pool.map(calculateIndicatorPipe_star, itertools.izip(self.pairs, itertools.repeat(predParam)))
-            pool.close()
-        else:
-            #prog = Tools.ProgressBar(0, len(self.pairs), 77) #Removed in traffic-intelligenc port
-            for j in xrange(len(self.pairs)):
-                #prog.updateAmount(j) #Removed in traffic-intelligenc port
-                collisionPoints, crossingZones = prediction.computeCrossingsCollisions(self.pairs[j].roadUser1, self.pairs[j].roadUser2, predParam, collisionDistanceThreshold, timeHorizon)      
-                
-                # Ignore empty collision points
-                empty = 1
-                for i in collisionPoints:
-                    if(collisionPoints[i] != []):
-                        empty = 0
-                if(empty == 1):
-                    self.pairs[j].hasCP = 0
-                else:
-                    self.pairs[j].hasCP = 1
-                self.pairs[j].CP = collisionPoints
-                
-                # Ignore empty crossing zones
-                empty = 1
-                for i in crossingZones:
-                    if(crossingZones[i] != []):
-                        empty = 0
-                if(empty == 1):
-                    self.pairs[j].hasCZ = 0
-                else:
-                    self.pairs[j].hasCZ = 1
-                self.pairs[j].CZ = crossingZones       
-                
-        for j in self.pairs:
-            self.interactionCount = self.interactionCount + len(j.CP)
-        self.CPcount = len(self.getCPlist())
-        self.Czcount = len(self.getCZlist())
-    
-    
-    def getPairsWCP(self):
-        lists = []
-        for j in self.pairs:
-            if(j.hasCP):
-                lists.append(j.num)
-        return lists
-        
-    def getPairsWCZ(self):
-        lists = []
-        for j in self.pairs:
-            if(j.hasCZ):
-                lists.append(j.num)
-        return lists
-    
-    def getCPlist(self,indicatorThreshold=float('Inf')):
-        lists = []
-        for j in self.pairs:
-            if(j.hasCP):
-                for k in j.CP:
-                    if(j.CP[k] != [] and j.CP[k][0].indicator < indicatorThreshold):
-                        lists.append([k,j.CP[k][0]])
-        return lists
-     
-    def getCZlist(self,indicatorThreshold=float('Inf')):
-        lists = []
-        for j in self.pairs:
-            if(j.hasCZ):
-                for k in j.CZ:
-                    if(j.CZ[k] != [] and j.CZ[k][0].indicator < indicatorThreshold):
-                        lists.append([k,j.CZ[k][0]])
-        return lists
-        
-    def genIndicatorHistogram(self, CPlist=False, bins=range(0,100,1)):
-        if(not CPlist):
-            CPlist = self.getCPlist()
-        if(not CPlist):
-            return False
-        TTC_list = []
-        for i in CPlist:
-            TTC_list.append(i[1].indicator)
-        histo = np.histogram(TTC_list,bins=bins)
-        histo += (histo[0].astype(float)/np.sum(histo[0]),)
-        return histo
 
 class Crossing(moving.STObject):
     '''Class for the event of a street crossing
--- a/python/indicators.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/indicators.py	Wed Jul 22 14:17:44 2015 -0400
@@ -2,6 +2,10 @@
 '''Class for indicators, temporal indicators, and safety indicators'''
 
 import moving
+#import matplotlib.nxutils as nx
+from matplotlib.pyplot import plot, ylim
+from matplotlib.pylab import find
+from numpy import array, arange, mean, floor, mean
 
 
 # need for a class representing the indicators, their units, how to print them in graphs...
@@ -15,21 +19,21 @@
 
     it should have more information like name, unit'''
     
-    def __init__(self, name, values, timeInterval=None, maxValue = None):
+    def __init__(self, name, values, timeInterval = None, maxValue = None):
         self.name = name
-        if timeInterval:
+        if timeInterval is None:
+            self.values = values
+            instants = sorted(self.values.keys())
+            if len(instants) > 0:
+                self.timeInterval = moving.TimeInterval(instants[0], instants[-1])
+            else:
+                self.timeInterval = moving.TimeInterval()
+        else:
             assert len(values) == timeInterval.length()
             self.timeInterval = timeInterval
             self.values = {}
             for i in xrange(int(round(self.timeInterval.length()))):
                 self.values[self.timeInterval[i]] = values[i]
-        else:
-            self.values = values
-            instants = sorted(self.values.keys())
-            if instants:
-                self.timeInterval = moving.TimeInterval(instants[0], instants[-1])
-            else:
-                self.timeInterval = moving.TimeInterval()
         self.maxValue = maxValue
 
     def __len__(self):
@@ -74,7 +78,6 @@
         return [self.__getitem__(t) for t in self.timeInterval]
 
     def plot(self, options = '', xfactor = 1., yfactor = 1., timeShift = 0, **kwargs):
-        from matplotlib.pylab import plot,ylim
         if self.getTimeInterval().length() == 1:
             marker = 'o'
         else:
@@ -141,9 +144,6 @@
         self.mostSevereIsMax = mostSevereIsMax
 
     def getMostSevereValue(self, minNInstants=1): # TODO use np.percentile
-        from matplotlib.mlab import find
-        from numpy.core.multiarray import array
-        from numpy.core.fromnumeric import mean
         values = array(self.values.values())
         indices = range(len(values))
         if len(indices) >= minNInstants:
@@ -152,6 +152,13 @@
         else:
             return None
 
+    def getInstantOfMostSevereValue(self):
+        '''Returns the instant at which the indicator reaches its most severe value'''
+        if self.mostSevereIsMax:
+            return max(self.values, key=self.values.get)
+        else:
+            return min(self.values, key=self.values.get)
+
 # functions to aggregate discretized maps of indicators
 # TODO add values in the cells between the positions (similar to discretizing vector graphics to bitmap)
 
@@ -163,7 +170,6 @@
 
     ex: speeds and trajectory'''
 
-    from numpy import floor, mean
     assert len(indicatorValues) == trajectory.length()
     indicatorMap = {}
     for k in xrange(trajectory.length()):
@@ -178,28 +184,22 @@
         indicatorMap[k] = mean(indicatorMap[k])
     return indicatorMap
 
-def indicatorMapFromPolygon(value, polygon, squareSize):
-    '''Fills an indicator map with the value within the polygon
-    (array of Nx2 coordinates of the polygon vertices)'''
-    import matplotlib.nxutils as nx
-    from numpy.core.multiarray import array, arange
-    from numpy import floor
-
-    points = []
-    for x in arange(min(polygon[:,0])+squareSize/2, max(polygon[:,0]), squareSize):
-        for y in arange(min(polygon[:,1])+squareSize/2, max(polygon[:,1]), squareSize):
-            points.append([x,y])
-    inside = nx.points_inside_poly(array(points), polygon)
-    indicatorMap = {}
-    for i in xrange(len(inside)):
-        if inside[i]:
-            indicatorMap[(floor(points[i][0]/squareSize), floor(points[i][1]/squareSize))] = 0
-    return indicatorMap
+# def indicatorMapFromPolygon(value, polygon, squareSize):
+#     '''Fills an indicator map with the value within the polygon
+#     (array of Nx2 coordinates of the polygon vertices)'''
+#     points = []
+#     for x in arange(min(polygon[:,0])+squareSize/2, max(polygon[:,0]), squareSize):
+#         for y in arange(min(polygon[:,1])+squareSize/2, max(polygon[:,1]), squareSize):
+#             points.append([x,y])
+#     inside = nx.points_inside_poly(array(points), polygon)
+#     indicatorMap = {}
+#     for i in xrange(len(inside)):
+#         if inside[i]:
+#             indicatorMap[(floor(points[i][0]/squareSize), floor(points[i][1]/squareSize))] = 0
+#     return indicatorMap
 
 def indicatorMapFromAxis(value, limits, squareSize):
     '''axis = [xmin, xmax, ymin, ymax] '''
-    from numpy.core.multiarray import arange
-    from numpy import floor
     indicatorMap = {}
     for x in arange(limits[0], limits[1], squareSize):
         for y in arange(limits[2], limits[3], squareSize):
@@ -210,7 +210,6 @@
     '''Puts many indicator maps together 
     (averaging the values in each cell 
     if more than one maps has a value)'''
-    #from numpy import mean
     indicatorMap = {}
     for m in maps:
         for k,v in m.iteritems():
--- a/python/ml.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/ml.py	Wed Jul 22 14:17:44 2015 -0400
@@ -6,25 +6,29 @@
 
 class Model(object):
     '''Abstract class for loading/saving model'''    
-    def load(self, fn):
-        self.model.load(fn)
+    def load(self, filename):
+        from os import path
+        if path.exists(filename):
+            self.model.load(filename)
+        else:
+            print('Provided filename {} does not exist: model not loaded!'.format(filename))
 
-    def save(self, fn):
-        self.model.save(fn)
+    def save(self, filename):
+        self.model.save(filename)
 
 class SVM(Model):
     '''wrapper for OpenCV SimpleVectorMachine algorithm'''
 
-    def __init__(self, svm_type, kernel_type, degree = 0, gamma = 1, coef0 = 0, Cvalue = 1, nu = 0, p = 0):
+    def __init__(self):
         import cv2
         self.model = cv2.SVM()
+
+    def train(self, samples, responses, svm_type, kernel_type, degree = 0, gamma = 1, coef0 = 0, Cvalue = 1, nu = 0, p = 0):
         self.params = dict(svm_type = svm_type, kernel_type = kernel_type, degree = degree, gamma = gamma, coef0 = coef0, Cvalue = Cvalue, nu = nu, p = p)
-
-    def train(self, samples, responses):
         self.model.train(samples, responses, params = self.params)
 
-    def predict(self, samples):
-        return np.float32([self.model.predict(s) for s in samples])
+    def predict(self, hog):
+        return self.model.predict(hog)
 
 
 class Centroid(object):
--- a/python/moving.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/moving.py	Wed Jul 22 14:17:44 2015 -0400
@@ -5,7 +5,7 @@
 from base import VideoFilenameAddable
 
 from math import sqrt, atan2, cos, sin
-from numpy import median, array, zeros, hypot, NaN, std
+from numpy import median, array, zeros, hypot, NaN, std, floor, float32
 from matplotlib.pyplot import plot
 from scipy.stats import scoreatpercentile
 from scipy.spatial.distance import cdist
@@ -78,7 +78,6 @@
         else:
             return None
 
-
 def unionIntervals(intervals):
     'returns the smallest interval containing all intervals'
     inter = intervals[0]
@@ -123,6 +122,9 @@
         '''Returns the length of the interval'''
         return float(max(0,self.last-self.first+1))
 
+    def __len__(self):
+        return self.length()
+
 # class BoundingPolygon:
 #     '''Class for a polygon bounding a set of points
 #     with methods to create intersection, unions...
@@ -142,11 +144,17 @@
         self.boundingPolygon = boundingPolygon
 
     def empty(self):
-        return self.timeInterval.empty() or not self.boudingPolygon
+        return self.timeInterval.empty()# or not self.boudingPolygon
 
     def getNum(self):
         return self.num
 
+    def __len__(self):
+        return self.timeInterval.length()
+
+    def length(self):
+        return self.timeInterval.length()
+
     def getFirstInstant(self):
         return self.timeInterval.first
 
@@ -190,8 +198,12 @@
         else:
             raise IndexError()
     
-    def orthogonal(self):
-        return Point(self.y, -self.x)
+    def orthogonal(self, clockwise = True):
+        'Returns the orthogonal vector'
+        if clockwise:
+            return Point(self.y, -self.x)
+        else:
+            return Point(-self.y, self.x)            
 
     def multiply(self, alpha):
         'Warning, returns a new Point'
@@ -1055,9 +1067,6 @@
             print 'The object does not exist at '+str(inter)
             return None
 
-    def length(self):
-        return self.timeInterval.length()
-
     def getPositions(self):
         return self.positions
 
@@ -1129,8 +1138,13 @@
             print('Object {} has no features loaded.'.format(self.getNum()))
             return None
 
-    def getSpeeds(self):
-        return self.getVelocities().norm()
+    def getSpeeds(self, nInstantsIgnoredAtEnds = 0):
+        speeds = self.getVelocities().norm()
+        if nInstantsIgnoredAtEnds > 0:
+            n = min(nInstantsIgnoredAtEnds, int(floor(self.length()/2.)))
+            return speeds[n:-n]
+        else:
+            return speeds
 
     def getSpeedIndicator(self):
         from indicators import SeverityIndicator
@@ -1346,23 +1360,20 @@
     ###
     # User Type Classification
     ###
-    def classifyUserTypeSpeedMotorized(self, threshold, aggregationFunc = median, ignoreNInstantsAtEnds = 0):
+    def classifyUserTypeSpeedMotorized(self, threshold, aggregationFunc = median, nInstantsIgnoredAtEnds = 0):
         '''Classifies slow and fast road users
         slow: non-motorized -> pedestrians
         fast: motorized -> cars
         
         aggregationFunc can be any function that can be applied to a vector of speeds, including percentile:
         aggregationFunc = lambda x: percentile(x, percentileFactor) # where percentileFactor is 85 for 85th percentile'''
-        if ignoreNInstantsAtEnds > 0:
-            speeds = self.getSpeeds()[ignoreNInstantsAtEnds:-ignoreNInstantsAtEnds]
-        else:
-            speeds = self.getSpeeds()
+        speeds = self.getSpeeds(nInstantsIgnoredAtEnds)
         if aggregationFunc(speeds) >= threshold:
             self.setUserType(userType2Num['car'])
         else:
             self.setUserType(userType2Num['pedestrian'])
 
-    def classifyUserTypeSpeed(self, speedProbabilities, aggregationFunc = median):
+    def classifyUserTypeSpeed(self, speedProbabilities, aggregationFunc = median, nInstantsIgnoredAtEnds = 0):
         '''Classifies road user per road user type
         speedProbabilities are functions return P(speed|class)
         in a dictionary indexed by user type names
@@ -1375,62 +1386,67 @@
         else:
         return x'''
         if not hasattr(self, 'aggregatedSpeed'):
-            self.aggregatedSpeed = aggregationFunc(self.getSpeeds())
+            self.aggregatedSpeed = aggregationFunc(self.getSpeeds(nInstantsIgnoredAtEnds))
         userTypeProbabilities = {}
         for userTypename in speedProbabilities:
             userTypeProbabilities[userType2Num[userTypename]] = speedProbabilities[userTypename](self.aggregatedSpeed)
         self.setUserType(utils.argmaxDict(userTypeProbabilities))
         return userTypeProbabilities
 
-    def initClassifyUserTypeHoGSVM(self, aggregationFunc = median):
+    def initClassifyUserTypeHoGSVM(self, aggregationFunc, pedBikeCarSVM, bikeCarSVM = None, pedBikeSpeedTreshold = float('Inf'), bikeCarSpeedThreshold = float('Inf'), nInstantsIgnoredAtEnds = 0):
         '''Initializes the data structures for classification
 
-        TODO? compute speed for longest feature?
-        Skip beginning and end of feature for speed? Offer options instead of median'''
-        self.aggregatedSpeed = aggregationFunc(self.getSpeeds())
+        TODO? compute speed for longest feature?'''
+        self.aggregatedSpeed = aggregationFunc(self.getSpeeds(nInstantsIgnoredAtEnds))
+        if self.aggregatedSpeed < pedBikeSpeedTreshold or bikeCarSVM is None:
+            self.appearanceClassifier = pedBikeCarSVM
+        elif self.aggregatedSpeed < bikeCarSpeedThreshold:
+            self.appearanceClassifier = bikeCarSVM
+        else:
+            class CarClassifier:
+                def predict(self, hog):
+                    return userType2Num['car']
+            self.appearanceClassifier = CarClassifier()
+        
         self.userTypes = {}
 
-    def classifyUserTypeHoGSVMAtInstant(self, img, pedBikeCarSVM, instant, homography, width, height, bikeCarSVM = None, pedBikeSpeedTreshold = float('Inf'), bikeCarSpeedThreshold = float('Inf'), px = 0.2, py = 0.2, pixelThreshold = 800):
+    def classifyUserTypeHoGSVMAtInstant(self, img, instant, homography, width, height, px = 0.2, py = 0.2, minNPixels = 800):
         '''Extract the image box around the object and 
         applies the SVM model on it'''
-        croppedImg, yCropMin, yCropMax, xCropMin, xCropMax = imageBox(img, self, instant, homography, width, height, px, py, pixelThreshold)
-        if len(croppedImg) > 0: # != []
-            hog = array([cvutils.HOG(croppedImg)], dtype = np.float32)
-            if self.aggregatedSpeed < pedBikeSpeedTreshold or bikeCarSVM is None:
-                self.userTypes[instant] = int(pedBikeCarSVM.predict(hog))
-            elif self.aggregatedSpeed < bikeCarSpeedTreshold:
-                self.userTypes[instant] = int(bikeCarSVM.predict(hog))
-            else:
-                self.userTypes[instant] = userType2Num['car']
+        croppedImg, yCropMin, yCropMax, xCropMin, xCropMax = cvutils.imageBox(img, self, instant, homography, width, height, px, py, minNPixels)
+        if croppedImg is not None and len(croppedImg) > 0:
+            hog = cvutils.HOG(croppedImg)#HOG(image, rescaleSize = (64, 64), orientations=9, pixelsPerCell=(8, 8), cellsPerBlock=(2, 2), visualize=False, normalize=False)
+            self.userTypes[instant] = int(self.appearanceClassifier.predict(hog))
         else:
             self.userTypes[instant] = userType2Num['unknown']
 
-    def classifyUserTypeHoGSVM(self, images, pedBikeCarSVM, homography, width, height, bikeCarSVM = None, pedBikeSpeedTreshold = float('Inf'), bikeCarSpeedThreshold = float('Inf'), speedProbabilities = None, aggregationFunc = median, px = 0.2, py = 0.2, pixelThreshold = 800):
+    def classifyUserTypeHoGSVM(self, pedBikeCarSVM = None, width = 0, height = 0, homography = None, images = None, bikeCarSVM = None, pedBikeSpeedTreshold = float('Inf'), bikeCarSpeedThreshold = float('Inf'), minSpeedEquiprobable = -1, speedProbabilities = None, aggregationFunc = median, nInstantsIgnoredAtEnds = 0, px = 0.2, py = 0.2, minNPixels = 800):
         '''Agregates SVM detections in each image and returns probability
         (proportion of instants with classification in each category)
 
-        iamges is a dictionary of images indexed by instant
+        images is a dictionary of images indexed by instant
         With default parameters, the general (ped-bike-car) classifier will be used
-        TODO? consider all categories?'''
-        if not hasattr(self, aggregatedSpeed) or not hasattr(self, userTypes):
+        
+        Considered categories are the keys of speedProbabilities'''
+        if not hasattr(self, 'aggregatedSpeed') or not hasattr(self, 'userTypes'):
             print('Initilize the data structures for classification by HoG-SVM')
-            self.initClassifyUserTypeHoGSVM(aggregationFunc)
+            self.initClassifyUserTypeHoGSVM(aggregationFunc, pedBikeCarSVM, bikeCarSVM, pedBikeSpeedTreshold, bikeCarSpeedThreshold, nInstantsIgnoredAtEnds)
 
-        if len(self.userTypes) != self.length(): # if classification has not been done previously
+        if len(self.userTypes) != self.length() and images is not None: # if classification has not been done previously
             for t in self.getTimeInterval():
                 if t not in self.userTypes:
-                    self.classifyUserTypeHoGSVMAtInstant(images[t], pedBikeCarSVM, t, homography, width, height, bikeCarSVM, pedBikeSpeedTreshold, bikeCarSpeedThreshold, px, py, pixelThreshold)
+                    self.classifyUserTypeHoGSVMAtInstant(images[t], t, homography, width, height, px, py, minNPixels)
         # compute P(Speed|Class)
-        if speedProbabilities is None: # equiprobable information from speed
+        if speedProbabilities is None or self.aggregatedSpeed < minSpeedEquiprobable: # equiprobable information from speed
             userTypeProbabilities = {userType2Num['car']: 1., userType2Num['pedestrian']: 1., userType2Num['bicycle']: 1.}
         else:
             userTypeProbabilities = {userType2Num[userTypename]: speedProbabilities[userTypename](self.aggregatedSpeed) for userTypename in speedProbabilities}
         # result is P(Class|Appearance) x P(Speed|Class)
-        nInstantsUserType = {userType2Num[userTypename]: 0 for userTypename in userTypeProbabilities}# number of instants the object is classified as userTypename
+        nInstantsUserType = {userTypeNum: 0 for userTypeNum in userTypeProbabilities}# number of instants the object is classified as userTypename
         for t in self.userTypes:
-            nInstantsUserType[self.userTypes[t]] += 1
-        for userTypename in userTypeProbabilities:
-            userTypeProbabilities[userTypename] *= nInstantsUserType[userTypename]
+            nInstantsUserType[self.userTypes[t]] = nInstantsUserType.get(self.userTypes[t], 0) + 1
+        for userTypeNum in userTypeProbabilities:
+            userTypeProbabilities[userTypeNum] *= nInstantsUserType[userTypeNum]
         # class is the user type that maximizes usertype probabilities
         self.setUserType(utils.argmaxDict(userTypeProbabilities))
 
--- a/python/prediction.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/prediction.py	Wed Jul 22 14:17:44 2015 -0400
@@ -6,6 +6,7 @@
 
 import math, random
 import numpy as np
+from multiprocessing import Pool
 
 
 class PredictedTrajectory(object):
@@ -137,8 +138,7 @@
 
     @staticmethod
     def computeExpectedIndicator(points):
-        from numpy import sum
-        return sum([p.indicator*p.probability for p in points])/sum([p.probability for p in points])
+        return np.sum([p.indicator*p.probability for p in points])/sum([p.probability for p in points])
 
 def computeCollisionTime(predictedTrajectory1, predictedTrajectory2, collisionDistanceThreshold, timeHorizon):
     '''Computes the first instant 
@@ -161,11 +161,11 @@
     from matplotlib.pyplot import figure, axis, title, close, savefig
     figure()
     for et in predictedTrajectories1:
-        et.predictPosition(timeHorizon)
+        et.predictPosition(int(np.round(timeHorizon)))
         et.plot('rx')
 
     for et in predictedTrajectories2:
-        et.predictPosition(timeHorizon)
+        et.predictPosition(int(np.round(timeHorizon)))
         et.plot('bx')
     obj1.plot('r')
     obj2.plot('b')
@@ -321,8 +321,8 @@
         if nProcesses == 1:
             if usePrototypes:
                 firstInstant= next( (x for x in xrange(commonTimeInterval.first,commonTimeInterval.last) if x-obj1.getFirstInstant() >= acceptPartialLength and x-obj2.getFirstInstant() >= acceptPartialLength), commonTimeInterval.last)
-                commonTimeIntervalList1= list(xrange(firstInstant,commonTimeInterval.last-1)) # do not look at the 1 last position/velocities, often with errors
-                commonTimeIntervalList2= list(xrange(firstInstant,commonTimeInterval.last-1,step)) # do not look at the 1 last position/velocities, often with errors
+                commonTimeIntervalList1= range(firstInstant,commonTimeInterval.last-1) # do not look at the 1 last position/velocities, often with errors
+                commonTimeIntervalList2= range(firstInstant,commonTimeInterval.last-1,step) # do not look at the 1 last position/velocities, often with errors
                 for i in commonTimeIntervalList2: 
                     i, cp, cz = self.computeCrossingsCollisionsAtInstant(i, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ, debug,usePrototypes,route1,route2,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)
                     if len(cp) != 0:
@@ -345,7 +345,6 @@
                     if len(cz) != 0:
                         crossingZones[i] = cz
         else:
-            from multiprocessing import Pool
             pool = Pool(processes = nProcesses)
             jobs = [pool.apply_async(computeCrossingsCollisionsAtInstant, args = (self, i, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ, debug,usePrototypes,route1,route2,prototypes,secondStepPrototypes,nMatching,objects,noiseEntryNums,noiseExitNums,minSimilarity,mostMatched,useDestination,useSpeedPrototype)) for i in list(commonTimeInterval)[:-1]]
             #results = [j.get() for j in jobs]
@@ -493,7 +492,8 @@
 
 class CVDirectPredictionParameters(PredictionParameters):
     '''Prediction parameters of prediction at constant velocity
-    using direct computation of the intersecting point'''
+    using direct computation of the intersecting point
+    Warning: the computed time to collision may be higher than timeHorizon (not used)'''
     
     def __init__(self):
         PredictionParameters.__init__(self, 'constant velocity (direct computation)', None)
@@ -533,19 +533,6 @@
                             crossingZones = [SafetyPoint(intersection, 1., timeInterval1.distance(timeInterval2))]
                     else:
                         collisionPoints = [SafetyPoint(intersection, 1., collisionTimeInterval.center())]
-            # elif computeCZ and (dot1 > 0 or dot2 > 0):
-            #     if dot1 > 0:
-            #         firstUser = obj2 # first through crossingzone
-            #         secondUser = obj1 # second through crossingzone
-            #     elif dot2 > 0:
-            #         firstUser = obj1
-            #         secondUser = obj2
-            #     p2 = secondUser.getPositionAtInstant(currentInstant)
-            #     v2 = secondUser.getVelocityAtInstant(currentInstant)
-            #     indices, intersections = firstUser.getPositions().getLineIntersections(p2, p2+v2)
-            #     if indices is not None:
-            #         pass
-            #     else: # one has to predict !!!
     
         if debug and intersection is not None:
             from matplotlib.pyplot import plot, figure, axis, title
@@ -562,12 +549,13 @@
 
 class CVExactPredictionParameters(PredictionParameters):
     '''Prediction parameters of prediction at constant velocity
-    using direct computation of the intersecting point (solving for the equation'''
+    using direct computation of the intersecting point (solving the equation)
+    Warning: the computed time to collision may be higher than timeHorizon (not used)'''
     
     def __init__(self):
         PredictionParameters.__init__(self, 'constant velocity (direct exact computation)', None)
 
-    def computeCrossingsCollisionsAtInstant(self, currentInstant, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ = False, debug = False):
+    def computeCrossingsCollisionsAtInstant(self, currentInstant, obj1, obj2, collisionDistanceThreshold, timeHorizon, computeCZ = False, debug = False, *kwargs):
         'TODO add collision point coordinates, compute pPET'
         #collisionPoints = []
         #crossingZones = []
@@ -581,9 +569,9 @@
         if intersection is not None:
             ttc = moving.Point.timeToCollision(p1, p2, v1, v2, collisionDistanceThreshold)
             if ttc:
-                return [SafetyPoint(intersection, 1., ttc)], [] # (p1+v1.multiply(ttc)+p2+v2.multiply(ttc)).multiply(0.5)
+                return currentInstant, [SafetyPoint(intersection, 1., ttc)], [] # (p1+v1.multiply(ttc)+p2+v2.multiply(ttc)).multiply(0.5)
             else:
-                return [],[]
+                return currentInstant, [],[]
 
 ####
 # Other Methods
--- a/python/storage.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/storage.py	Wed Jul 22 14:17:44 2015 -0400
@@ -6,6 +6,7 @@
 from base import VideoFilenameAddable
 
 import sqlite3, logging
+from numpy import log, min as npmin, max as npmax, round as npround, array, sum as npsum, loadtxt
 
 
 commentChar = '#'
@@ -289,22 +290,16 @@
     connection.close()
     return matched_indexes
 
-def getTrajectoryIdQuery(objectNumbers, trajectoryType):
-    if trajectoryType == 'feature':
-        statementBeginning = 'where trajectory_id '
-    elif trajectoryType == 'object':
-        statementBeginning = 'and OF.object_id '
-    elif trajectoryType == 'bbtop' or 'bbbottom':
-        statementBeginning = 'where object_id '
-    else:
-        print('no trajectory type was chosen')
-
+def getObjectCriteria(objectNumbers):
     if objectNumbers is None:
         query = ''
     elif type(objectNumbers) == int:
-        query = statementBeginning+'between 0 and {0} '.format(objectNumbers)
+        query = 'between 0 and {0}'.format(objectNumbers-1)
     elif type(objectNumbers) == list:
-        query = statementBeginning+'in ('+', '.join([str(n) for n in objectNumbers])+') '
+        query = 'in ('+', '.join([str(n) for n in objectNumbers])+')'
+    else:
+        print('objectNumbers {} are not a known type ({})'.format(objectNumbers, type(objectNumbers)))
+        query = ''
     return query
 
 def loadTrajectoriesFromTable(connection, tableName, trajectoryType, objectNumbers = None):
@@ -315,13 +310,19 @@
     cursor = connection.cursor()
 
     try:
-        idQuery = getTrajectoryIdQuery(objectNumbers, trajectoryType)
+        objectCriteria = getObjectCriteria(objectNumbers)
         if trajectoryType == 'feature':
-            queryStatement = 'SELECT * from '+tableName+' '+idQuery+'ORDER BY trajectory_id, frame_number'
+            queryStatement = 'SELECT * from '+tableName
+            if objectNumbers is not None:
+                queryStatement += ' where trajectory_id '+objectCriteria
+            queryStatement += ' ORDER BY trajectory_id, frame_number'
             cursor.execute(queryStatement)
             logging.debug(queryStatement)
         elif trajectoryType == 'object':
-            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 '+idQuery+'group by OF.object_id, P.frame_number ORDER BY OF.object_id, P.frame_number'
+            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'
+            if objectNumbers is not None:
+                queryStatement += ' and OF.object_id '+objectCriteria
+            queryStatement += ' group by OF.object_id, P.frame_number ORDER BY OF.object_id, P.frame_number'
             cursor.execute(queryStatement)
             logging.debug(queryStatement)
         elif trajectoryType in ['bbtop', 'bbbottom']:
@@ -329,7 +330,10 @@
                 corner = 'top_left'
             elif trajectoryType == 'bbbottom':
                 corner = 'bottom_right'
-            queryStatement = 'SELECT object_id, frame_number, x_'+corner+', y_'+corner+' FROM '+tableName+' '+idQuery+'ORDER BY object_id, frame_number'
+            queryStatement = 'SELECT object_id, frame_number, x_'+corner+', y_'+corner+' FROM '+tableName
+            if objectNumbers is not None:
+                queryStatement += ' where object_id '+objectCriteria
+            queryStatement += ' ORDER BY object_id, frame_number'
             cursor.execute(queryStatement)
             logging.debug(queryStatement)
         else:
@@ -361,17 +365,17 @@
     return objects
 
 def loadUserTypesFromTable(cursor, trajectoryType, objectNumbers):
-    objectIdQuery = getTrajectoryIdQuery(objectNumbers, trajectoryType)
-    if objectIdQuery == '':
-        cursor.execute('SELECT object_id, road_user_type from objects')
-    else:
-        cursor.execute('SELECT object_id, road_user_type from objects where '+objectIdQuery[7:])
+    objectCriteria = getObjectCriteria(objectNumbers)
+    queryStatement = 'SELECT object_id, road_user_type from objects'
+    if objectNumbers is not None:
+        queryStatement += ' where object_id '+objectCriteria
+    cursor.execute(queryStatement)
     userTypes = {}
     for row in cursor:
         userTypes[row[0]] = row[1]
     return userTypes
 
-def loadTrajectoriesFromSqlite(filename, trajectoryType, objectNumbers = None):
+def loadTrajectoriesFromSqlite(filename, trajectoryType, objectNumbers = None, withFeatures = False):
     '''Loads the first objectNumbers objects or the indices in objectNumbers from the database'''
     connection = sqlite3.connect(filename)
 
@@ -390,8 +394,11 @@
         cursor = connection.cursor()
         try:
             # attribute feature numbers to objects
-            objectIdQuery = getTrajectoryIdQuery(objectNumbers, trajectoryType)
-            queryStatement = 'SELECT P.trajectory_id, OF.object_id from positions P, objects_features OF where P.trajectory_id = OF.trajectory_id '+objectIdQuery+'group by P.trajectory_id order by OF.object_id' # order is important to group all features per object
+            objectCriteria = getObjectCriteria(objectNumbers)
+            queryStatement = 'SELECT P.trajectory_id, OF.object_id from positions P, objects_features OF where P.trajectory_id = OF.trajectory_id'
+            if objectNumbers is not None:
+                queryStatement += ' and OF.object_id '+objectCriteria
+            queryStatement += ' group by P.trajectory_id order by OF.object_id' # order is important to group all features per object
             cursor.execute(queryStatement) 
             logging.debug(queryStatement)
 
@@ -410,6 +417,14 @@
             userTypes = loadUserTypesFromTable(cursor, trajectoryType, objectNumbers)
             for obj in objects:
                 obj.userType = userTypes[obj.getNum()]
+
+            if withFeatures:
+                nFeatures = 0
+                for obj in objects:
+                    nFeatures = max(nFeatures, max(obj.featureNumbers))
+                features = loadTrajectoriesFromSqlite(filename, 'feature', nFeatures+1)
+                for obj in objects:
+                    obj.setFeatures(features)
              
         except sqlite3.OperationalError as error:
             printDBError(error)
@@ -512,7 +527,7 @@
     connection = sqlite3.connect(filename)
     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')
+        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 = {}
@@ -521,13 +536,14 @@
                 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]:
+            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)
-                indicatorTypeNum = row[5]
+                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 []
@@ -656,20 +672,21 @@
 
     if usePandas:
         from pandas import read_csv
-        from numpy import min, max, round
         data = read_csv(filename, delimiter=';', comment='*', header=0, skiprows = 1)
         generatePDLaneColumn(data)
         data['TIME'] = data['$VEHICLE:SIMSEC']*simulationStepsPerTimeUnit
         if warmUpLastInstant is not None:
             data = data[data['TIME']>=warmUpLastInstant]
         grouped = data.loc[:,['NO','TIME']].groupby(['NO'], as_index = False)
-        instants = grouped['TIME'].agg({'first': min, 'last': max})
+        instants = grouped['TIME'].agg({'first': npmin, 'last': npmax})
         for row_index, row in instants.iterrows():
             objNum = int(row['NO'])
             tmp = data[data['NO'] == objNum]
             objects[objNum] = moving.MovingObject(num = objNum, timeInterval = moving.TimeInterval(row['first'], row['last']))
             # positions should be rounded to nDecimals decimals only
-            objects[objNum].curvilinearPositions = moving.CurvilinearTrajectory(S = round(tmp['POS'].tolist(), nDecimals), Y = round(tmp['POSLAT'].tolist(), nDecimals), lanes = tmp['LANE'].tolist())
+            objects[objNum].curvilinearPositions = moving.CurvilinearTrajectory(S = npround(tmp['POS'].tolist(), nDecimals), Y = npround(tmp['POSLAT'].tolist(), nDecimals), lanes = tmp['LANE'].tolist())
+            if nObjects > 0 and len(objects) >= nObjects:
+                break
         return objects.values()
     else:
         inputfile = openCheck(filename, quitting = True)
@@ -718,7 +735,6 @@
     If lanes is not None, only the data for the selected lanes will be provided
     (format as string x_y where x is link index and y is lane index)'''
     from pandas import read_csv
-    from numpy import array, sum as npsum
     columns = ['NO', '$VEHICLE:SIMSEC', 'POS']
     if lanes is not None:
         columns += ['LANE\LINK\NO', 'LANE\INDEX']
@@ -727,7 +743,6 @@
     data.sort(['$VEHICLE:SIMSEC'], inplace = True)
 
     nStationary = 0
-    from matplotlib.pyplot import plot, figure
     nVehicles = 0
     for name, group in data.groupby(['NO'], sort = False):
         nVehicles += 1
@@ -754,8 +769,9 @@
 
     nCollisions = 0
     for name, group in merged.groupby(['LANE\LINK\NO', 'LANE\INDEX', 'NO_x', 'NO_y']):
-        diff = group['POS_x']-group['POS_y']
-        if len(diff) >= 2 and min(diff) < 0 and max(diff) > 0:
+        diff = group['POS_x'].convert_objects(convert_numeric=True)-group['POS_y'].convert_objects(convert_numeric=True)
+        # 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
+        if len(diff) >= 2 and npmin(diff) < 0 and npmax(diff) > 0:
             xidx = diff[diff < 0].argmax()
             yidx = diff[diff > 0].argmin()
             if abs(group.loc[xidx, '$VEHICLE:SIMSEC'] - group.loc[yidx, '$VEHICLE:SIMSEC']) <= collisionTimeDifference:
@@ -874,7 +890,6 @@
 
     def loadConfigFile(self, filename):
         from ConfigParser import ConfigParser
-        from numpy import loadtxt
         from os import path
 
         config = ConfigParser()
@@ -901,7 +916,21 @@
         self.videoFrameRate = config.getfloat(self.sectionHeader, 'video-fps')
 
         # Classification parameters
-        
+        self.speedAggregationMethod = config.get(self.sectionHeader, 'speed-aggregation-method')
+        self.nFramesIgnoreAtEnds = config.getint(self.sectionHeader, 'nframes-ignore-at-ends')
+        self.speedAggregationQuantile = config.getint(self.sectionHeader, 'speed-aggregation-quantile')
+        self.minSpeedEquiprobable = config.getfloat(self.sectionHeader, 'min-speed-equiprobable')
+        self.minNPixels = config.getint(self.sectionHeader, 'min-npixels-crop')
+        self.pedBikeCarSVMFilename = config.get(self.sectionHeader, 'pbv-svm-filename')
+        self.bikeCarSVMFilename = config.get(self.sectionHeader, 'bv-svm-filename')
+        self.maxPedestrianSpeed = config.getfloat(self.sectionHeader, 'max-ped-speed')
+        self.maxCyclistSpeed = config.getfloat(self.sectionHeader, 'max-cyc-speed')
+        self.meanPedestrianSpeed = config.getfloat(self.sectionHeader, 'mean-ped-speed')
+        self.stdPedestrianSpeed = config.getfloat(self.sectionHeader, 'std-ped-speed')
+        self.locationCyclistSpeed = config.getfloat(self.sectionHeader, 'cyc-speed-loc')
+        self.scaleCyclistSpeed = config.getfloat(self.sectionHeader, 'cyc-speed-scale')
+        self.meanVehicleSpeed = config.getfloat(self.sectionHeader, 'mean-veh-speed')
+        self.stdVehicleSpeed = config.getfloat(self.sectionHeader, 'std-veh-speed')
 
         # Safety parameters
         self.maxPredictedSpeed = config.getfloat(self.sectionHeader, 'max-predicted-speed')/3.6/self.videoFrameRate
@@ -921,6 +950,26 @@
         if filename is not None:
             self.loadConfigFile(filename)
 
+    def convertToFrames(self, speedRatio = 3.6):
+        '''Converts parameters with a relationship to time in 'native' frame time
+        speedRatio is the conversion from the speed unit in the config file
+        to the distance per second
+
+        ie param(config file) = speedRatio x fps x param(used in program)
+        eg km/h = 3.6 (m/s to km/h) x frame/s x m/frame'''
+        denominator = self.videoFrameRate*speedRatio
+        denominator2 = denominator**2
+        self.minSpeedEquiprobable = self.minSpeedEquiprobable/denominator
+        self.maxPedestrianSpeed = self.maxPedestrianSpeed/denominator
+        self.maxCyclistSpeed = self.maxCyclistSpeed/denominator
+        self.meanPedestrianSpeed = self.meanPedestrianSpeed/denominator
+        self.stdPedestrianSpeed = self.stdPedestrianSpeed/denominator
+        self.meanVehicleSpeed = self.meanVehicleSpeed/denominator
+        self.stdVehicleSpeed = self.stdVehicleSpeed/denominator
+        # special case for the lognormal distribution
+        self.locationCyclistSpeed = self.locationCyclistSpeed-log(denominator)
+        #self.scaleCyclistSpeed = self.scaleCyclistSpeed
+
 class SceneParameters(object):
     def __init__(self, config, sectionName):
         from ConfigParser import NoOptionError
--- a/python/tests/moving.txt	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/tests/moving.txt	Wed Jul 22 14:17:44 2015 -0400
@@ -167,13 +167,10 @@
 >>> t.differentiate().empty()
 True
 
->>> o1 = MovingObject(positions = Trajectory([[0]*3,[2]*3]), velocities = Trajectory([[0]*3,[1]*3]))
+>>> o1 = MovingObject.generate(Point(0., 2.), Point(0., 1.), TimeInterval(0,2))
 >>> o1.classifyUserTypeSpeedMotorized(0.5, np.median)
 >>> userTypeNames[o1.getUserType()]
 'car'
->>> o1.classifyUserTypeSpeedMotorized(0.5, np.mean)
->>> userTypeNames[o1.getUserType()]
-'car'
 >>> o1.classifyUserTypeSpeedMotorized(1.5, np.median)
 >>> userTypeNames[o1.getUserType()]
 'pedestrian'
--- a/python/tests/utils.txt	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/tests/utils.txt	Wed Jul 22 14:17:44 2015 -0400
@@ -50,7 +50,7 @@
 >>> mostCommon([range(2), range(4), range(2)])
 [0, 1]
 
->>> lcss = LCSS(lambda x,y: abs(x-y) <= 0.1)
+>>> lcss = LCSS(similarityFunc = lambda x,y: abs(x-y) <= 0.1)
 >>> lcss.compute(range(5), range(5))
 5
 >>> lcss.compute(range(1,5), range(5))
@@ -59,8 +59,6 @@
 0
 >>> lcss.compute(range(5), range(10))
 5
->>> lcss.compute(range(5), range(10), 2)
-5
 >>> lcss.similarityFunc = lambda x,y: x == y
 >>> lcss.compute(['a','b','c'], ['a','b','c', 'd'])
 3
@@ -117,3 +115,14 @@
 8
 >>> lcss.subSequenceIndices
 [(2, 0), (4, 1), (6, 2), (7, 3), (8, 4), (9, 5), (11, 6), (13, 7)]
+
+>>> lcss = LCSS(metric = 'cityblock', epsilon = 0.1)
+>>> lcss.compute([[i] for i in range(5)], [[i] for i in range(5)])
+5
+>>> lcss.compute([[i] for i in range(1,5)], [[i] for i in range(5)])
+4
+>>> lcss.compute([[i] for i in range(5,10)], [[i] for i in range(5)])
+0
+>>> lcss.compute([[i] for i in range(5)], [[i] for i in range(10)])
+5
+
--- a/python/utils.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/python/utils.py	Wed Jul 22 14:17:44 2015 -0400
@@ -6,6 +6,7 @@
 from datetime import time, datetime
 from math import sqrt, ceil, floor
 from scipy.stats import kruskal, shapiro
+from scipy.spatial import distance
 from numpy import zeros, array, exp, sum as npsum, int as npint, arange, cumsum, median, isnan, ones, convolve,  dtype, isnan, NaN, mean, ma
 
 
@@ -26,6 +27,13 @@
 # Simple statistics
 #########################
 
+def logNormalMeanVar(loc, scale):
+    '''location and scale are respectively the mean and standard deviation of the normal in the log-normal distribution
+    https://en.wikipedia.org/wiki/Log-normal_distribution'''
+    mean = exp(loc+(scale**2)/2)
+    var = (exp(loc**2)-1)*exp(2*loc+scale**2)
+    return mean, var
+
 def sampleSize(stdev, tolerance, percentConfidence, printLatex = False):
     from scipy.stats.distributions import norm
     k = round(norm.ppf(0.5+percentConfidence/200., 0, 1)*100)/100. # 1.-(100-percentConfidence)/200.
@@ -232,6 +240,7 @@
     return ceil(v*tens)/tens
 
 def inBetween(bound1, bound2, x):
+    'useful if one does not know the order of bound1/bound2'
     return bound1 <= x <= bound2 or bound2 <= x <= bound1
 
 def pointDistanceL2(x1,y1,x2,y2):
@@ -619,24 +628,55 @@
 
 class LCSS(object):
     '''Class that keeps the LCSS parameters
-    and puts together the various computations'''
-    def __init__(self, similarityFunc, delta = float('inf'), aligned = False, lengthFunc = min):
-        self.similarityFunc = similarityFunc
-        self.aligned = aligned
-        self.delta = delta
-        self.lengthFunc = lengthFunc
-        self.subSequenceIndices = [(0,0)]
+    and puts together the various computations
+
+    the methods with names starting with _ are not to be shadowed
+    in child classes, who will shadow the other methods, 
+    ie compute and computeXX methods'''
+    def __init__(self, similarityFunc = None, metric = None, epsilon = None, delta = float('inf'), aligned = False, lengthFunc = min):
+        '''One should provide either a similarity function
+        that indicates (return bool) whether elements in the compares lists are similar
+
+        eg distance(p1, p2) < epsilon
+        
+        or a type of metric usable in scipy.spatial.distance.cdist with an epsilon'''
+        if similarityFunc is None and metric is None:
+            print("No way to compute LCSS, similarityFunc and metric are None. Exiting")
+            import sys
+            sys.exit()
+        elif metric is not None and epsilon is None:
+            print("Please provide a value for epsilon if using a cdist metric. Exiting")
+            import sys
+            sys.exit()
+        else:
+            self.similarityFunc = similarityFunc
+            self.metric = metric
+            self.epsilon = epsilon
+            self.aligned = aligned
+            self.delta = delta
+            self.lengthFunc = lengthFunc
+            self.subSequenceIndices = [(0,0)]
 
     def similarities(self, l1, l2, jshift=0):
         n1 = len(l1)
         n2 = len(l2)
         self.similarityTable = zeros((n1+1,n2+1), dtype = npint)
-        for i in xrange(1,n1+1):
-            for j in xrange(max(1,i-jshift-self.delta),min(n2,i-jshift+self.delta)+1):
-                if self.similarityFunc(l1[i-1], l2[j-1]):
-                    self.similarityTable[i,j] = self.similarityTable[i-1,j-1]+1
-                else:
-                    self.similarityTable[i,j] = max(self.similarityTable[i-1,j], self.similarityTable[i,j-1])
+        if self.similarityFunc is not None:
+            for i in xrange(1,n1+1):
+                for j in xrange(max(1,i-jshift-self.delta),min(n2,i-jshift+self.delta)+1):
+                    if self.similarityFunc(l1[i-1], l2[j-1]):
+                        self.similarityTable[i,j] = self.similarityTable[i-1,j-1]+1
+                    else:
+                        self.similarityTable[i,j] = max(self.similarityTable[i-1,j], self.similarityTable[i,j-1])
+        elif self.metric is not None:
+            similarElements = distance.cdist(l1, l2, self.metric) <= self.epsilon
+            for i in xrange(1,n1+1):
+                for j in xrange(max(1,i-jshift-self.delta),min(n2,i-jshift+self.delta)+1):
+                    if similarElements[i-1, j-1]:
+                        self.similarityTable[i,j] = self.similarityTable[i-1,j-1]+1
+                    else:
+                        self.similarityTable[i,j] = max(self.similarityTable[i-1,j], self.similarityTable[i,j-1])
+            
 
     def subSequence(self, i, j):
         '''Returns the subsequence of two sequences
@@ -652,12 +692,11 @@
 
     def _compute(self, _l1, _l2, computeSubSequence = False):
         '''returns the longest common subsequence similarity
-        based on the threshold on distance between two elements of lists l1, l2
-        similarityFunc returns True or False whether the two points are considered similar
+        l1 and l2 should be the right format
+        eg list of tuple points for cdist 
+        or elements that can be compare using similarityFunc
 
         if aligned, returns the best matching if using a finite delta by shifting the series alignments
-
-        eg distance(p1, p2) < epsilon
         '''
         if len(_l2) < len(_l1): # l1 is the shortest
             l1 = _l2
--- a/scripts/classify-objects.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/scripts/classify-objects.py	Wed Jul 22 14:17:44 2015 -0400
@@ -1,23 +1,116 @@
 #! /usr/bin/env python
 
+import cvutils, moving, ml, storage
+
 import numpy as np
 import sys, argparse
-from cv2 import SVM_RBF, SVM_C_SVC
+#from cv2 import SVM_RBF, SVM_C_SVC
+import cv2
+from scipy.stats import norm, lognorm
 
-import cvutils, moving, ml
+# TODO add mode detection live
 
 parser = argparse.ArgumentParser(description='The program processes indicators for all pairs of road users in the scene')
 parser.add_argument('--cfg', dest = 'configFilename', help = 'name of the configuration file', required = True)
-parser.add_argument('-d', dest = 'directoryName', help = 'name of the parent directory containing the videos and extracted trajectories to process', required = True)
-# parser.add_argument('-o', dest = 'homographyFilename', help = 'name of the image to world homography file')
-# need a classification config file for speed distribution parameters, svm models, frequency parameters, area parameters etc
-#parser.add_argument('--cfg', dest = 'svmType', help = 'SVM type', default = SVM_C_SVC, type = long)
-
-
-#parser.add_argument('-s', dest = 'rescaleSize', help = 'rescale size of image samples', default = 64, type = int)
-#parser.add_argument('-o', dest = 'nOrientations', help = 'number of orientations in HoG', default = 9, type = int)
-#parser.add_argument('-p', dest = 'nPixelsPerCell', help = 'number of pixels per cell', default = 8, type = int)
-#parser.add_argument('-c', dest = 'nCellsPerBlock', help = 'number of cells per block', default = 2, type = int)
+parser.add_argument('-d', dest = 'databaseFilename', help = 'name of the Sqlite database file (overrides the configuration file)')
+parser.add_argument('-i', dest = 'videoFilename', help = 'name of the video file (overrides the configuration file)')
+parser.add_argument('-n', dest = 'nObjects', help = 'number of objects to classify', type = int, default = None)
+parser.add_argument('--plot-speed-distributions', dest = 'plotSpeedDistribution', help = 'simply plots the distributions used for each user type', action = 'store_true')
+parser.add_argument('--max-speed-distribution-plot', dest = 'maxSpeedDistributionPlot', help = 'if plotting the user distributions, the maximum speed to display', type = float, default = 50.)
 
 args = parser.parse_args()
 params = storage.ProcessParameters(args.configFilename)
+
+if args.videoFilename is not None:
+    videoFilename = args.videoFilename
+else:
+    videoFilename = params.videoFilename
+if args.databaseFilename is not None:
+    databaseFilename = args.databaseFilename
+else:
+    databaseFilename = params.databaseFilename
+
+params.convertToFrames(3.6)
+if params.homography is not None:
+    invHomography = np.linalg.inv(params.homography)
+
+if params.speedAggregationMethod == 'median':
+    speedAggregationFunc = np.median
+elif params.speedAggregationMethod == 'mean':
+    speedAggregationFunc = np.mean
+elif params.speedAggregationMethod == 'quantile':
+    speedAggregationFunc = lambda speeds: np.percentile(speeds, args.speedAggregationQuantile)
+else:
+    print('Unknown speed aggregation method: {}. Exiting'.format(params.speedAggregationMethod))
+    sys.exit()
+
+pedBikeCarSVM = ml.SVM()
+pedBikeCarSVM.load(params.pedBikeCarSVMFilename)
+bikeCarSVM = ml.SVM()
+bikeCarSVM.load(params.bikeCarSVMFilename)
+
+# log logistic for ped and bik otherwise ((pedBeta/pedAlfa)*((sMean/pedAlfa)**(pedBeta-1)))/((1+(sMean/pedAlfa)**pedBeta)**2.)
+speedProbabilities = {'car': lambda s: norm(params.meanVehicleSpeed, params.stdVehicleSpeed).pdf(s),
+                      'pedestrian': lambda s: norm(params.meanPedestrianSpeed, params.stdPedestrianSpeed).pdf(s), 
+                      'bicycle': lambda s: lognorm(params.scaleCyclistSpeed, loc = 0., scale = np.exp(params.locationCyclistSpeed)).pdf(s)} # numpy lognorm shape, loc, scale: shape for numpy is scale (std of the normal) and scale for numpy is location (mean of the normal)
+
+if args.plotSpeedDistribution:
+    import matplotlib.pyplot as plt
+    plt.figure()
+    for k in speedProbabilities:
+        plt.plot(np.arange(0.1, args.maxSpeedDistributionPlot, 0.1), [speedProbabilities[k](s/3.6/25) for s in np.arange(0.1, args.maxSpeedDistributionPlot, 0.1)], label = k)
+    plt.xlabel('Speed (km/h)')
+    plt.ylabel('Probability')
+    plt.legend()
+    plt.title('Probability Density Function')
+    plt.show()
+    sys.exit()
+
+objects = storage.loadTrajectoriesFromSqlite(databaseFilename, 'object', args.nObjects, withFeatures = True)
+#features = storage.loadTrajectoriesFromSqlite(databaseFilename, 'feature')
+intervals = []
+for obj in objects:
+    #obj.setFeatures(features)
+    intervals.append(obj.getTimeInterval())
+timeInterval = moving.unionIntervals(intervals)
+
+capture = cv2.VideoCapture(videoFilename)
+width = int(capture.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH))
+height = int(capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))
+
+pastObjects = []
+if params.undistort: # setup undistortion
+    [map1, map2] = computeUndistortMaps(width, height, undistortedImageMultiplication, intrinsicCameraMatrix, distortionCoefficients)
+if capture.isOpened():
+    ret = True
+    frameNum = timeInterval.first
+    capture.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, frameNum)
+    lastFrameNum = timeInterval.last
+
+    while ret and frameNum <= lastFrameNum:
+        ret, img = capture.read()
+        if ret:
+            if frameNum%50 == 0:
+                print('frame number: {}'.format(frameNum))
+                currentObjects = []
+                for obj in objects:
+                    if obj.getLastInstant() < frameNum:
+                        obj.classifyUserTypeHoGSVM(minSpeedEquiprobable = params.minSpeedEquiprobable, speedProbabilities = speedProbabilities)
+                        pastObjects.append(obj)
+                    else:
+                        currentObjects.append(obj)
+                objects = currentObjects
+            if params.undistort:
+                img = cv2.remap(img, map1, map2, interpolation=cv2.INTER_LINEAR)
+            for obj in objects:
+                if obj.existsAtInstant(frameNum):
+                    if obj.getFirstInstant() == frameNum:
+                        obj.initClassifyUserTypeHoGSVM(speedAggregationFunc, pedBikeCarSVM, bikeCarSVM, params.maxPedestrianSpeed, params.maxCyclistSpeed, params.nFramesIgnoreAtEnds)
+                    obj.classifyUserTypeHoGSVMAtInstant(img, frameNum, invHomography, width, height, 0.2, 0.2, 800) # px, py, pixelThreshold
+        frameNum += 1
+    
+    for obj in objects:
+        obj.classifyUserTypeHoGSVM(minSpeedEquiprobable = params.minSpeedEquiprobable, speedProbabilities = speedProbabilities)
+        pastObjects.append(obj)
+    print('Saving user types')
+    storage.setRoadUserTypes(databaseFilename, pastObjects)
--- a/scripts/train-object-classification.py	Wed Jul 22 14:17:19 2015 -0400
+++ b/scripts/train-object-classification.py	Wed Jul 22 14:17:44 2015 -0400
@@ -36,31 +36,32 @@
 
 for k, v in imageDirectories.iteritems():
     print('Loading {} samples'.format(k))
-    trainingSamplesPBV[k], trainingLabelsPBV[k] = cvutils.createHOGTrainingSet(v, moving.userType2Num[k], rescaleSize, args.nOrientations, nPixelsPerCell, nCellsPerBlock)
+    trainingSamples, trainingLabels = cvutils.createHOGTrainingSet(v, moving.userType2Num[k], rescaleSize, args.nOrientations, nPixelsPerCell, nCellsPerBlock)
+    trainingSamplesPBV[k], trainingLabelsPBV[k] = trainingSamples, trainingLabels
     if k != 'pedestrian':
-	trainingSamplesBV[k], trainingLabelsBV[k] = cvutils.createHOGTrainingSet(v, moving.userType2Num[k], rescaleSize, args.nOrientations, nPixelsPerCell, nCellsPerBlock)
+	trainingSamplesBV[k], trainingLabelsBV[k] = trainingSamples, trainingLabels
     if k != 'car':
-	trainingSamplesPB[k], trainingLabelsPB[k] = cvutils.createHOGTrainingSet(v, moving.userType2Num[k], rescaleSize, args.nOrientations, nPixelsPerCell, nCellsPerBlock)
+	trainingSamplesPB[k], trainingLabelsPB[k] = trainingSamples, trainingLabels
     if k != 'bicycle':
-	trainingSamplesPV[k], trainingLabelsPV[k] = cvutils.createHOGTrainingSet(v, moving.userType2Num[k], rescaleSize, args.nOrientations, nPixelsPerCell, nCellsPerBlock)
+	trainingSamplesPV[k], trainingLabelsPV[k] = trainingSamples, trainingLabels
 
 # Training the Support Vector Machine
 print "Training Pedestrian-Cyclist-Vehicle Model"
-model = ml.SVM(args.svmType, args.kernelType)
-model.train(np.concatenate(trainingSamplesPBV.values()), np.concatenate(trainingLabelsPBV.values()))
+model = ml.SVM()
+model.train(np.concatenate(trainingSamplesPBV.values()), np.concatenate(trainingLabelsPBV.values()), args.svmType, args.kernelType)
 model.save(args.directoryName + "/modelPBV.xml")
 
 print "Training Cyclist-Vehicle Model"
-model = ml.SVM(args.svmType, args.kernelType)
-model.train(np.concatenate(trainingSamplesBV.values()), np.concatenate(trainingLabelsBV.values()))
+model = ml.SVM()
+model.train(np.concatenate(trainingSamplesBV.values()), np.concatenate(trainingLabelsBV.values()), args.svmType, args.kernelType)
 model.save(args.directoryName + "/modelBV.xml")
 
 print "Training Pedestrian-Cyclist Model"
-model = ml.SVM(args.svmType, args.kernelType)
-model.train(np.concatenate(trainingSamplesPB.values()), np.concatenate(trainingLabelsPB.values()))
+model = ml.SVM()
+model.train(np.concatenate(trainingSamplesPB.values()), np.concatenate(trainingLabelsPB.values()), args.svmType, args.kernelType)
 model.save(args.directoryName + "/modelPB.xml")
 
 print "Training Pedestrian-Vehicle Model"
-model = ml.SVM(args.svmType, args.kernelType)
-model.train(np.concatenate(trainingSamplesPV.values()), np.concatenate(trainingLabelsPV.values()))
+model = ml.SVM()
+model.train(np.concatenate(trainingSamplesPV.values()), np.concatenate(trainingLabelsPV.values()), args.svmType, args.kernelType)
 model.save(args.directoryName + "/modelPV.xml")
--- a/scripts/uninstall-scripts.sh	Wed Jul 22 14:17:19 2015 -0400
+++ b/scripts/uninstall-scripts.sh	Wed Jul 22 14:17:44 2015 -0400
@@ -1,7 +1,15 @@
 #!/bin/sh
-installDirname='/usr/local/bin'
+if [ $# -ge 1 ];
+then
+    installDirname=$1
+    echo 'Removing from directory '$installDirname
+else
+    installDirname='/usr/local/bin'
+    echo 'Removing from default directory '$installDirname
+fi
+
 for filename in *
 do
     echo 'rm '$installDirname/$filename
     rm $installDirname/$filename
-done
\ No newline at end of file
+done
--- a/tracking.cfg	Wed Jul 22 14:17:19 2015 -0400
+++ b/tracking.cfg	Wed Jul 22 14:17:44 2015 -0400
@@ -81,23 +81,33 @@
 # minimum average number of features per frame to create a vehicle hypothesis
 min-nfeatures-group = 3
 # Road user classification
+# min number of pixels in cropped image to classify by SVM
+min-npixels-crop = 400
+# method to aggregate road user speed
+speed-aggregation-method = median
+# number of frames to ignore at both ends of a series (noisy)
+nframes-ignore-at-ends = 2
+# quantile for the speed aggregation, if quantile is chosen
+speed-aggregation-quantile = 50
+# speed value below which all classes are equiprobable (distributions give odd values there) (km/h)
+min-speed-equiprobable = 3.33
 # filename of the general ped/cyc/veh SVM classifier
 pbv-svm-filename = modelPBV.xml
 # filename of the cyc/veh SVM classifier
-pbv-svm-filename = modelBV.xml
-# maximum pedestrian speed (agregate: mean, median, 85th centile, etc.) 3.6 m/s
-max-ped-speed = 0.12
-# maximum cyclist speed (agregate: mean, median, 85th centile, etc.) 10.8 m/s (3xped)
-max-cyc-speed = 0.36
-# mean pedestrian speed and standard deviation (in a normal distribution) 1.36+-0.24 m/s
-mean-ped-speed = 0.45
-std-ped-speed = 0.008
-# mean cyclist speed and standard deviation (in a log-normal distribution) 1.36+-0.24 m/s
-mean-cyc-speed = 0.45
-std-cyc-speed = 0.008
-# mean vehicle speed and standard deviation (in a normal distribution) 5.12+-2.11 m/s
-mean-veh-speed = 0.17
-std-veh-speed = 0.07
+bv-svm-filename = modelBV.xml
+# maximum pedestrian speed (agregate: mean, median, 85th centile, etc.) 10 km/h
+max-ped-speed = 10.0
+# maximum cyclist speed (agregate: mean, median, 85th centile, etc.) 30 km/h (3xped)
+max-cyc-speed = 30.0
+# mean pedestrian speed and standard deviation (in a normal distribution) 4.91+-0.88 km/h
+mean-ped-speed = 4.91
+std-ped-speed = 0.88
+# mean cyclist speed and standard deviation (in a log-normal distribution) 11.+-4.83 km/h
+cyc-speed-loc = 2.31
+cyc-speed-scale = 0.42
+# mean vehicle speed and standard deviation (in a normal distribution) 18.45+-7.6 km/h
+mean-veh-speed = 18.45
+std-veh-speed = 7.6
 # Safety analysis
 # maximum speed when predicting future motion (km/h)
 max-predicted-speed = 50