# This code is authored by Julie R. Williamson and John Williamson.
# Julie.Williamson@glasgow.ac.uk, JohnH.Williamson@glasgow.ac.uk
# If using this code or the associated materials, please cite this source.
# The original publication is available at:
# http://juliericowilliamson.com/blog/wp-content/uploads/2014/05/Williamson-small.pdf
# Williamson, J.R. and Williamson, J. Analysing Pedestrian Traffic Around Public Displays.
# In the Proceedings of Pervasive Displays 2014. ACM, New York, USA.
# The MIT License (MIT)
# Copyright (c) <year> <copyright holders>
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Please ensure the following dependencies are installed before use:
import pylab
import numpy as np
import itertools
import sys, getopt
import scipy.signal, scipy.interpolate
w,h=640,360
border=20
table_x, table_y = 315, 200
[docs]def interactive_trails(traces):
valid_trails = []
for id,t in traces:
pts = np.array(list(t))
invalid = True
# bad starting point
if (pts[0,1]>border and pts[0,1]<w-border) and (pts[0,2]>border and pts[0,2]<h-border):
invalid = True
# bad ending point
if (pts[-1,1]>border and pts[-1,1]<w-border) and (pts[-1,2]>border and pts[-1,2]<h-border):
invalid = True
# terminate around table
if (pts[-1,2] > h-border) and (pts[-1,1] > w/2-75 and pts[-1,1] < w/2+75):
invalid = False
fit = np.polyfit(pts[:,1], pts[:,2], 1)
ys = np.polyval(fit, pts[:,1])
rmse = np.sqrt(np.mean((ys-pts[:,2])**2))
if rmse<2:
invalid = True
# too short
if len(pts) < 250:
invalid = True
if not invalid:
pts[:,1] = np.clip(pts[:,1],0,w)
pts[:,2] = np.clip(pts[:,2],0,h)
valid_trails.append(pts)
return valid_trails
[docs]def min_validate_trails(traces):
valid_trails = []
for id,t in traces:
pts = np.array(list(t))
invalid = False
# # bad starting point
# if (pts[0,1]>border and pts[0,1]<w-border) and (pts[0,2]>border and pts[0,2]<h-border):
# invalid = True
# # bad ending point
# if (pts[-1,1]>border and pts[-1,1]<w-border) and (pts[-1,2]>border and pts[-1,2]<h-border):
# invalid = True
# fit = np.polyfit(pts[:,1], pts[:,2], 1)
# ys = np.polyval(fit, pts[:,1])
# rmse = np.sqrt(np.mean((ys-pts[:,2])**2))
# if rmse<2:
# invalid = True
# # If line is straight, invalid
# fit = np.polyfit(pts[:,1], pts[:,2], 1)
# ys = np.polyval(fit, pts[:,1])
# rmse = np.sqrt(np.mean((ys-pts[:,2])**2))
# if rmse<2:
# invalid = True
# if rmse>=30:
# invalid = True
# too short
# if len(pts) < 200:
# invalid = True
# total_distance = np.sqrt((pts[0,1]-pts[-1,1])**2+(pts[0,2]-pts[-1,2])**2)
# if total_distance<300:
# invalid = True
if not invalid:
pts[:,1] = np.clip(pts[:,1],0,w)
pts[:,2] = np.clip(pts[:,2],0,h)
valid_trails.append(pts)
return valid_trails
[docs]def fake_validate(traces, straight):
valid_trails = []
for id,t in traces:
pts = np.array(list(t))
invalid = False
# # bad starting point
# if (pts[0,1]>border and pts[0,1]<w-border) and (pts[0,2]>border and pts[0,2]<h-border):
# invalid = True
# # bad ending point
# if (pts[-1,1]>border and pts[-1,1]<w-border) and (pts[-1,2]>border and pts[-1,2]<h-border):
# invalid = True
# terminate around table
#if (pts[-1,2] > h-border) and (pts[-1,1] > w/2-75 and pts[-1,1] < w/2+75):
# invalid = False
fit = np.polyfit(pts[:,1], pts[:,2], 1)
ys = np.polyval(fit, pts[:,1])
rmse = np.sqrt(np.mean((ys-pts[:,2])**2))
if rmse<2:
invalid = True
if straight:
if rmse>=30:
invalid = True
else:
if rmse<30:
invalid = True
# too short
if len(pts) < 200:
invalid = True
total_distance = np.sqrt((pts[0,1]-pts[-1,1])**2+(pts[0,2]-pts[-1,2])**2)
#print total_distance
if total_distance<300:
invalid = True
if not invalid:
pts[:,1] = np.clip(pts[:,1],0,w)
pts[:,2] = np.clip(pts[:,2],0,h)
valid_trails.append(pts)
return valid_trails
[docs]def validate_trails(traces):
valid_trails = []
for id,t in traces:
pts = np.array(list(t))
invalid = False
# bad starting point
if (pts[0,1]>border and pts[0,1]<w-border) and (pts[0,2]>border and pts[0,2]<h-border):
invalid = True
# bad ending point
if (pts[-1,1]>border and pts[-1,1]<w-border) and (pts[-1,2]>border and pts[-1,2]<h-border):
invalid = True
fit = np.polyfit(pts[:,1], pts[:,2], 1)
ys = np.polyval(fit, pts[:,1])
rmse = np.sqrt(np.mean((ys-pts[:,2])**2))
if rmse<2:
invalid = True
# too short
if len(pts) < 150:
invalid = True
# terminate around table
if ((pts[0,2] > h-border) and (pts[0,1] > w/2-75 and pts[0,1] < w/2+75) or (pts[-1,2] > h-border) and (pts[-1,1] > w/2-75 and pts[-1,1] < w/2+75)):
invalid = True
if not invalid:
pts[:,1] = np.clip(pts[:,1],0,w)
pts[:,2] = np.clip(pts[:,2],0,h)
valid_trails.append(pts)
return valid_trails
[docs]def test_trails(traces):
valid_trails = []
for id,t in traces:
pts = np.array(list(t))
invalid = False
# bad starting point
if (pts[0,1]>border and pts[0,1]<w-border) and (pts[0,2]>border and pts[0,2]<h-border):
invalid = True
# bad ending point
if (pts[-1,1]>border and pts[-1,1]<w-border) and (pts[-1,2]>border and pts[-1,2]<h-border):
invalid = False
fit = np.polyfit(pts[:,1], pts[:,2], 1)
ys = np.polyval(fit, pts[:,1])
rmse = np.sqrt(np.mean((ys-pts[:,2])**2))
if rmse<2:
invalid = False
# too short
if len(pts) < 100:
invalid = True
# terminate around table
if ((pts[0,2] > h-border) and (pts[0,1] > w/2-75 and pts[0,1] < w/2+75) or (pts[-1,2] > h-border) and (pts[-1,1] > w/2-75 and pts[-1,1] < w/2+75)):
invalid = False
if not invalid:
pts[:,1] = np.clip(pts[:,1],0,w)
pts[:,2] = np.clip(pts[:,2],0,h)
valid_trails.append(pts)
return valid_trails
[docs]def get_velocity(trails):
velocities = []
smoothing = scipy.signal.get_window('hann', 30)
smoothing = smoothing / np.sum(smoothing)
for trail in trails:
a,b = trail.shape
p = np.zeros((a,b+4))
p[:,0:4] = trail
p[:,4] = np.gradient(trail[:,1])
p[:,5] = np.gradient(trail[:,2])
p[:,6] = np.sqrt(p[:,4]**2+p[:,5]**2)
p[:,6] = scipy.signal.convolve(p[:,6], smoothing, mode='same')
p[:,7] = np.arctan2(p[:,1], p[:,2])
velocities.append(p)
return velocities
[docs]def velocity_plot(csvfile):
trace = np.loadtxt(csvfile, comments=';', delimiter=',')
traces = itertools.groupby(trace, lambda x:x[0])
valid_trails = min_validate_trails(traces)
valid_trails = get_velocity(valid_trails)
trails = np.vstack(valid_trails)
x,y = np.meshgrid(np.arange(0,w,20), np.arange(0,h,20))
interp = scipy.interpolate.griddata((trails[:,1], trails[:,2]), trails[:,5]/trails[:,6], (x,y))
pylab.imshow(interp, cmap='gist_heat', interpolation='nearest')
pylab.show()
[docs]def draw_trails(trails, background=None, speed_range=None, y_range=None, colour=(0,0,1), spline=True, table_approach=False, draw_raw=False, line_width=2, draw_a=1):
pylab.subplot(2,1,1)
num_trails = 0
if background!=None:
pylab.imshow(background)
for trail in trails:
#pylab.plot(trail[:,1], trail[:,2], alpha=0.2, c='b')
mean_speed = np.mean(trail[:,6])
mean_y = np.mean(trail[:,2])
use_trail = True
if not(not speed_range or (speed_range[0]<mean_speed<speed_range[1])):
use_trail = False
if y_range and (mean_y<y_range[0] or mean_y>y_range[1]):
use_trail = False
if spline:
x = trail[:,1]
y = trail[:,2]
t = np.arange(len(trail))
spx = scipy.interpolate.UnivariateSpline(t,x,s=1e4,k=4)
spy = scipy.interpolate.UnivariateSpline(t,y,s=1e4,k=4)
spline_x = spx(t)
spline_y = spy(t)
else:
spline_x = trail[:,1]
spline_y = trail[:,2]
if use_trail:
num_trails += 1
end_distance = np.sqrt((trail[-1,1]-table_x)**2 + (trail[-1,2]-table_y)**2)
alpha = np.clip(end_distance**-2*2000.0-0.05,0,1)
if table_approach:
#pylab.plot(x, y, alpha=alpha, c=(0,.3,1), lw=2)
if alpha > .2:
pylab.plot(spline_x, spline_y, alpha=alpha, c=colour, lw=line_width)
print alpha
else:
pylab.plot(spline_x, spline_y, alpha=draw_a, c=colour, lw=line_width)
if draw_raw:
pylab.plot(x, y, alpha=draw_a, c=colour, lw=line_width)
pylab.ylim(h-border,border)
pylab.xlim(border,w-border)
# if y_range:
# pylab.axhline(y_range[0],c='g')
# pylab.axhline(y_range[1],c='g')
pylab.axis('off')
pylab.subplot(2,1,2)
speeds = [np.mean(trail[:,6]) for trail in trails]
pylab.hist(speeds, bins=20)
if speed_range:
pylab.axvline(speed_range[0],c='r')
pylab.axvline(speed_range[1],c='r')
return num_trails
[docs]def plot_trails(csvfile, backgroundfile='trails_480.png'):
background = pylab.imread(backgroundfile)
# CC Data
trace = np.loadtxt(csvfile, comments=';', delimiter=',')
traces = itertools.groupby(trace, lambda x:x[0])
valid_trails = get_velocity(min_validate_trails(traces))
# People Approaching the Display
pylab.figure()
all_trails = draw_trails(valid_trails, background=background, colour=(1, 0, 0), table_approach=True)
# All Trails
pylab.figure()
all_trails = draw_trails(valid_trails, background=background, colour=(1, 1, 0), line_width=2, draw_a=.2)
print all_trails
# # Figure 1: Chance Participants
# trace = np.loadtxt(csvfile, comments=';', delimiter=',')
# traces = itertools.groupby(trace, lambda x:x[0])
# vtraces = get_velocity(fake_validate(traces, straight=True))
# pylab.figure()
# draw_trails(vtraces, background=background, colour=(1,0,0), table_approach=True)
# # Figure 2: Active Participants
# trace = np.loadtxt(csvfile, comments=';', delimiter=',')
# traces = itertools.groupby(trace, lambda x:x[0])
# ctraces = get_velocity(fake_validate(traces, straight=False))
# pylab.figure()
# draw_trails(ctraces, background=background, colour=(1,0,1), table_approach=True)
# # All Non-Interacting Users
# trace = np.loadtxt(csvfile, comments=';', delimiter=',')
# traces = itertools.groupby(trace, lambda x:x[0])
# valid_trails = get_velocity(validate_trails(traces))
# pylab.figure()
# draw_trails(valid_trails, background=background, colour=(0, .8, 1))
# # VALIDATION
# trace = np.loadtxt(csvfile, comments=';', delimiter=',')
# traces = itertools.groupby(trace, lambda x:x[0])
# valid_trails = get_velocity(min_validate_trails(traces))
# #
# pylab.figure()
# all_trails = draw_trails(valid_trails, background=background, colour=(1, 0, 0))
# # Interested Avoiders
# pylab.figure()
# interested = draw_trails(valid_trails, background=background, speed_range=[0, 2.2], y_range=[90,230], colour=(0,1,0))
# # Disinterested Avoiders
# pylab.figure()
# disinterested = draw_trails(valid_trails, background=background, speed_range=[2.5, 3.2], y_range=[0,170], colour=(0,1,1))
# # Disinterested Avoiders
# pylab.figure()
# busker = draw_trails(valid_trails, background=background, y_range=[0,120], colour=(1,1,1))
# # Assertive Avoiders
# pylab.figure()
# assertive = draw_trails(valid_trails, background=background, speed_range=[2.5, 3.2], y_range=[230,400], colour=(1,.5,0))
# # All within the table Avoiders
# pylab.figure()
# close_by = draw_trails(valid_trails, background=background, y_range=[230,400], colour=(1,1,0))
# print "Chance passerby" , len(vtraces)
# print "Active passerby" , len(ctraces)
# print "Valid Trails" , len(valid_trails)
# print "Red Trails", all_trails
# print "Green Trails" , interested
# print "Disinterested" , disinterested
# print "Busker" , busker
# print "Assertive" , assertive
# ys = []
# for trail in valid_trails:
# ys.append(np.mean(trail[:,2]))
# print "Average Y for Non-Interacting", np.mean(ys)
# print "Number Of Trails within Table", close_by
pylab.show()
if __name__=="__main__":
background = '/Users/julierwilliamson/Dropbox/ocv_python/Datas/trails_480.png'
traces = '/Users/julierwilliamson/Dropbox/ocv_python/Extras/test_traces.csv'
try:
opts,args = getopt.getopt(sys.argv[1:], "f:b:")
except getopt.GetoptError:
print "Getopt Error"
exit(2)
for opt, arg in opts:
if opt == "-f":
traces = arg
elif opt == "-b":
background = arg
#background = '/Users/julierwilliamson/Movies/Pedestrian Data/September 6 Dr Duck/dr_duck_background.png'
#traces = '/Users/julierwilliamson/Movies/Pedestrian Data/Validation/validation_4_traces_ext.csv'
# background = '/Users/julierwilliamson/Movies/Pedestrian Data/September 10 Bubble Pop/bubble_pop_background.png'
# traces = '/Users/julierwilliamson/Movies/Pedestrian Data/September 10 Bubble Pop/bubble_pop_traces.csv'
# background = '/Users/julierwilliamson/Movies/Pedestrian Data/September 9 Lost In Waves/lost_in_waves_background.png'
# traces = '/Users/julierwilliamson/Movies/Pedestrian Data/September 9 Lost In Waves/lost_in_waves_traces.csv'
# background = '/Users/julierwilliamson/Movies/Pedestrian Data/September 5 Busking/busker_background.png'
# traces = '/Users/julierwilliamson/Movies/Pedestrian Data/September 5 Busking/busker_traces.csv'
#background = '/Users/julierwilliamson/Movies/Pedestrian Data/August 9 Baseline/baseline_background.png'
#traces = '/Users/julierwilliamson/Movies/Pedestrian Data/August 9 Baseline/traces_baseline_aug_9.csv'
# background = '/Users/julierwilliamson/Movies/Pedestrian Data/August 22 LEDS_noninteractive/rainbow_chase_background.png'
# traces = '/Users/julierwilliamson/Movies/Pedestrian Data/August 22 LEDS_noninteractive/rainbow_chase_traces.csv'
print "Background: ", background
print "Traces:" , traces
plot_trails(traces, background)
#velocity_plot(traces)