158 lines
5.5 KiB
Python
158 lines
5.5 KiB
Python
|
'''
|
||
|
(*)~----------------------------------------------------------------------------------
|
||
|
Pupil - eye tracking platform
|
||
|
Copyright (C) 2012-2015 Pupil Labs
|
||
|
|
||
|
Distributed under the terms of the CC BY-NC-SA License.
|
||
|
License details are in the file license.txt, distributed as part of this software.
|
||
|
----------------------------------------------------------------------------------~(*)
|
||
|
'''
|
||
|
|
||
|
import os
|
||
|
import cv2
|
||
|
import numpy as np
|
||
|
#logging
|
||
|
import logging
|
||
|
logger = logging.getLogger(__name__)
|
||
|
from file_methods import save_object
|
||
|
|
||
|
|
||
|
def correlate_data(data,timestamps):
|
||
|
'''
|
||
|
data: dict of data :
|
||
|
will have at least:
|
||
|
timestamp: float
|
||
|
|
||
|
timestamps: timestamps list to correlate data to
|
||
|
|
||
|
this takes a data list and a timestamps list and makes a new list
|
||
|
with the length of the number of timestamps.
|
||
|
Each slot conains a list that will have 0, 1 or more assosiated data points.
|
||
|
|
||
|
Finnaly we add an index field to the data_point with the assosiated index
|
||
|
'''
|
||
|
timestamps = list(timestamps)
|
||
|
data_by_frame = [[] for i in timestamps]
|
||
|
|
||
|
frame_idx = 0
|
||
|
data_index = 0
|
||
|
|
||
|
|
||
|
while True:
|
||
|
try:
|
||
|
datum = data[data_index]
|
||
|
# we can take the midpoint between two frames in time: More appropriate for SW timestamps
|
||
|
ts = ( timestamps[frame_idx]+timestamps[frame_idx+1] ) / 2.
|
||
|
# or the time of the next frame: More appropriate for Sart Of Exposure Timestamps (HW timestamps).
|
||
|
# ts = timestamps[frame_idx+1]
|
||
|
except IndexError:
|
||
|
# we might loose a data point at the end but we dont care
|
||
|
break
|
||
|
|
||
|
if datum['timestamp'] <= ts:
|
||
|
datum['index'] = frame_idx
|
||
|
data_by_frame[frame_idx].append(datum)
|
||
|
data_index +=1
|
||
|
else:
|
||
|
frame_idx+=1
|
||
|
|
||
|
return data_by_frame
|
||
|
|
||
|
|
||
|
|
||
|
def update_recording_0v4_to_current(rec_dir):
|
||
|
logger.info("Updatig recording from v0.4x format to current version")
|
||
|
gaze_array = np.load(os.path.join(rec_dir,'gaze_positions.npy'))
|
||
|
pupil_array = np.load(os.path.join(rec_dir,'pupil_positions.npy'))
|
||
|
gaze_list = []
|
||
|
pupil_list = []
|
||
|
|
||
|
for datum in pupil_array:
|
||
|
ts, confidence, id, x, y, diameter = datum[:6]
|
||
|
pupil_list.append({'timestamp':ts,'confidence':confidence,'id':id,'norm_pos':[x,y],'diameter':diameter})
|
||
|
|
||
|
pupil_by_ts = dict([(p['timestamp'],p) for p in pupil_list])
|
||
|
|
||
|
for datum in gaze_array:
|
||
|
ts,confidence,x,y, = datum
|
||
|
gaze_list.append({'timestamp':ts,'confidence':confidence,'norm_pos':[x,y],'base':[pupil_by_ts.get(ts,None)]})
|
||
|
|
||
|
pupil_data = {'pupil_positions':pupil_list,'gaze_positions':gaze_list}
|
||
|
try:
|
||
|
save_object(pupil_data,os.path.join(rec_dir, "pupil_data"))
|
||
|
except IOError:
|
||
|
pass
|
||
|
|
||
|
def update_recording_0v3_to_current(rec_dir):
|
||
|
logger.info("Updatig recording from v0.3x format to current version")
|
||
|
pupilgaze_array = np.load(os.path.join(rec_dir,'gaze_positions.npy'))
|
||
|
gaze_list = []
|
||
|
pupil_list = []
|
||
|
|
||
|
for datum in pupilgaze_array:
|
||
|
gaze_x,gaze_y,pupil_x,pupil_y,ts,confidence = datum
|
||
|
#some bogus size and confidence as we did not save it back then
|
||
|
pupil_list.append({'timestamp':ts,'confidence':confidence,'id':0,'norm_pos':[pupil_x,pupil_y],'diameter':50})
|
||
|
gaze_list.append({'timestamp':ts,'confidence':confidence,'norm_pos':[gaze_x,gaze_y],'base':[pupil_list[-1]]})
|
||
|
|
||
|
pupil_data = {'pupil_positions':pupil_list,'gaze_positions':gaze_list}
|
||
|
try:
|
||
|
save_object(pupil_data,os.path.join(rec_dir, "pupil_data"))
|
||
|
except IOError:
|
||
|
pass
|
||
|
|
||
|
def is_pupil_rec_dir(rec_dir):
|
||
|
if not os.path.isdir(rec_dir):
|
||
|
logger.error("No valid dir supplied")
|
||
|
return False
|
||
|
meta_info_path = os.path.join(rec_dir,"info.csv")
|
||
|
try:
|
||
|
with open(meta_info_path) as info:
|
||
|
meta_info = dict( ((line.strip().split('\t')) for line in info.readlines() ) )
|
||
|
info = meta_info["Capture Software Version"]
|
||
|
except:
|
||
|
logger.error("Could not read info.csv file: Not a valid Pupil recording.")
|
||
|
return False
|
||
|
return True
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
def transparent_circle(img,center,radius,color,thickness):
|
||
|
center = tuple(map(int,center))
|
||
|
rgb = [255*c for c in color[:3]] # convert to 0-255 scale for OpenCV
|
||
|
alpha = color[-1]
|
||
|
radius = int(radius)
|
||
|
if thickness > 0:
|
||
|
pad = radius + 2 + thickness
|
||
|
else:
|
||
|
pad = radius + 3
|
||
|
roi = slice(center[1]-pad,center[1]+pad),slice(center[0]-pad,center[0]+pad)
|
||
|
|
||
|
try:
|
||
|
overlay = img[roi].copy()
|
||
|
cv2.circle(overlay,(pad,pad), radius=radius, color=rgb, thickness=thickness, lineType=cv2.cv.CV_AA)
|
||
|
opacity = alpha
|
||
|
cv2.addWeighted(overlay, opacity, img[roi], 1. - opacity, 0, img[roi])
|
||
|
except:
|
||
|
logger.debug("transparent_circle would have been partially outsize of img. Did not draw it.")
|
||
|
|
||
|
|
||
|
def transparent_image_overlay(pos,overlay_img,img,alpha):
|
||
|
"""
|
||
|
Overlay one image with another with alpha blending
|
||
|
In player this will be used to overlay the eye (as overlay_img) over the world image (img)
|
||
|
Arguments:
|
||
|
pos: (x,y) position of the top left corner in numpy row,column format from top left corner (numpy coord system)
|
||
|
overlay_img: image to overlay
|
||
|
img: destination image
|
||
|
alpha: 0.0-1.0
|
||
|
"""
|
||
|
roi = slice(pos[1],pos[1]+overlay_img.shape[0]),slice(pos[0],pos[0]+overlay_img.shape[1])
|
||
|
try:
|
||
|
cv2.addWeighted(overlay_img,alpha,img[roi],1.-alpha,0,img[roi])
|
||
|
except:
|
||
|
logger.debug("transparent_image_overlay was outside of the world image and was not drawn")
|
||
|
pass
|
||
|
|