changeset 680:da1352b89d02 dev

classification is working
author Nicolas Saunier <nicolas.saunier@polymtl.ca>
date Fri, 05 Jun 2015 02:25:30 +0200
parents 97c305108460
children acce61a1edc8
files python/cvutils.py python/ml.py python/moving.py python/storage.py python/utils.py scripts/classify-objects.py scripts/train-object-classification.py tracking.cfg
diffstat 8 files changed, 213 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/python/cvutils.py	Wed Jun 03 16:00:46 2015 +0200
+++ b/python/cvutils.py	Fri Jun 05 02:25:30 2015 +0200
@@ -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)
 
@@ -200,7 +200,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 +235,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,7 +256,7 @@
         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 = []
@@ -538,10 +541,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 +554,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/ml.py	Wed Jun 03 16:00:46 2015 +0200
+++ b/python/ml.py	Fri Jun 05 02:25:30 2015 +0200
@@ -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 Jun 03 16:00:46 2015 +0200
+++ b/python/moving.py	Fri Jun 05 02:25:30 2015 +0200
@@ -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]
@@ -1129,8 +1128,9 @@
             print('Object {} has no features loaded.'.format(self.getNum()))
             return None
 
-    def getSpeeds(self):
-        return self.getVelocities().norm()
+    def getSpeeds(self, nInstantsIgnoredAtEnds = 0):
+        n = min(nInstantsIgnoredAtEnds, int(floor(self.length()/2.)))
+        return self.getVelocities().norm()[n:-n]
 
     def getSpeedIndicator(self):
         from indicators import SeverityIndicator
@@ -1346,23 +1346,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 +1372,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 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/storage.py	Wed Jun 03 16:00:46 2015 +0200
+++ b/python/storage.py	Fri Jun 05 02:25:30 2015 +0200
@@ -6,6 +6,7 @@
 from base import VideoFilenameAddable
 
 import sqlite3, logging
+from numpy import log
 
 
 commentChar = '#'
@@ -901,7 +902,20 @@
         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.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 +935,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/utils.py	Wed Jun 03 16:00:46 2015 +0200
+++ b/python/utils.py	Fri Jun 05 02:25:30 2015 +0200
@@ -26,6 +26,11 @@
 # Simple statistics
 #########################
 
+def logNormalMeanVar(loc, scale):
+    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.
--- a/scripts/classify-objects.py	Wed Jun 03 16:00:46 2015 +0200
+++ b/scripts/classify-objects.py	Fri Jun 05 02:25:30 2015 +0200
@@ -1,23 +1,85 @@
 #! /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('-u', dest = 'undistort', help = 'undistort the video (because features have been extracted that way)', action = 'store_true')
+#parser.add_argument('-f', dest = 'firstFrameNum', help = 'number of first frame number to display', type = int)
+#parser.add_argument('--last-frame', dest = 'lastFrameNum', help = 'number of last frame number to save (for image saving, no display is made)', type = int)
+# parser.add_argument('--min-speed-equiprobable', dest = 'minSpeedEquiprobable', help = 'speed value below which all classes are equiprobable (distributions give odd values there) (km/h)', type = float, default = 3.33)
+# parser.add_argument('--speed-aggregation', dest = 'speedAggregationMethod', help = 'method to aggregate road user speed', type = str, choices = ['median', 'mean', 'quantile'], default = 'median')
+# parser.add_argument('--speed-aggregation-quantile', dest = 'speedAggregationQuantile', help = 'quantile for the speed aggregation, if quantile is chosen', type = int, default = 50)
 
 args = parser.parse_args()
 params = storage.ProcessParameters(args.configFilename)
+
+params.convertToFrames(3.6)
+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))
+    from sys import exit
+    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)} # lognorm shape, loc, scale
+
+objects = storage.loadTrajectoriesFromSqlite(params.databaseFilename, 'object')
+features = storage.loadTrajectoriesFromSqlite(params.databaseFilename, 'feature')
+intervals = []
+for obj in objects:
+    obj.setFeatures(features)
+    intervals.append(obj.getTimeInterval())
+timeInterval = moving.unionIntervals(intervals)
+
+capture = cv2.VideoCapture(params.videoFilename)
+width = int(capture.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH))
+height = int(capture.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))
+
+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))
+            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:
+                        print 'first frame for obj {}'.format(obj.getNum())
+                        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)
--- a/scripts/train-object-classification.py	Wed Jun 03 16:00:46 2015 +0200
+++ b/scripts/train-object-classification.py	Fri Jun 05 02:25:30 2015 +0200
@@ -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/tracking.cfg	Wed Jun 03 16:00:46 2015 +0200
+++ b/tracking.cfg	Fri Jun 05 02:25:30 2015 +0200
@@ -84,20 +84,28 @@
 # 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
+# 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
+# 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