Skip to content
Branch: master
Find file Copy path
Find file Copy path
2 contributors

Users who have contributed to this file

@AlexEMG @FedeClaudi
192 lines (145 sloc) 7.36 KB
import numpy as np
from collections import namedtuple
from scipy.spatial import distance
Functions to extract time spent by the mouse in each of a list of user defined ROIS
Contributed by Federico Claudi
Example usage:
rois --> a dictionary with name and position of each roi
tracking --> a pandas dataframe with X,Y,Velocity for each bodypart
bodyparts --> a list with the name of all the bodyparts
results = {}
for bp in bodyparts:
bp_tracking = np.array((tracking.bp.x.values, tracking.bp.y.values, tracking.bp.Velocity.values))
res = get_timeinrois_stats(bp_tracking, roi, fps=30)
results[bp] = res
if Velocity is not know, it can be calculated using "calc_distance_between_points_in_a_vector_2d":
vel = calc_distance_between_points_in_a_vector_2d(np.array(tracking.bp.x.values, tracking.bp.y.values))
which returns a 1d vector with the velocity in pixels/frame [effectively the number pixels a tracked point moved
from one frame to the next]
def calc_distance_between_points_in_a_vector_2d(v1):
'''calc_distance_between_points_in_a_vector_2d [for each consecutive pair of points, p1-p2, in a vector, get euclidian distance]
This function can be used to calculate the velocity in pixel/frame from tracking data (X,Y coordinates)
v1 {[np.array]} -- [2d array, X,Y position at various timepoints]
[np.array] -- [1d array with distance at each timepoint]
>>> v1 = [0, 10, 25, 50, 100]
>>> d = calc_distance_between_points_in_a_vector_2d(v1)
# Check data format
if isinstance(v1, dict) or not np.any(v1) or v1 is None:
raise ValueError(
'Feature not implemented: cant handle with data format passed to this function')
# If pandas series were passed, try to get numpy arrays
v1, v2 = v1.values, v2.values
except: # all good
# loop over each pair of points and extract distances
dist = []
for n, pos in enumerate(v1):
# Get a pair of points
if n == 0: # get the position at time 0, velocity is 0
p0 = pos
p1 = pos # get position at current frame
# Calc distance
dist.append(np.abs(distance.euclidean(p0, p1)))
# Prepare for next iteration, current position becomes the old one and repeat
p0 = p1
return np.array(dist)
def get_roi_at_each_frame(bp_data, rois):
Given position data for a bodypart and the position of a list of rois, this function calculates which roi is
the closest to the bodypart at each frame
:param bp_data: numpy array: [nframes, 2] -> X,Y position of bodypart at each frame
[as extracted by DeepLabCut] --> df.bodypart.values
:param rois: dictionary with the position of each roi. The position is stored in a named tuple with the location of
two points defyining the roi: topleft(X,Y) and bottomright(X,Y).
:return: tuple, closest roi to the bodypart at each frame
if not isinstance(rois, dict): raise ValueError('rois locations should be passed as a dictionary')
if not isinstance(bp_data, np.ndarray):
if not isinstance(bp_data, tuple): raise ValueError('Unrecognised data format for bp tracking data')
pos = np.zeros((len(bp_data.x), 2))
pos[:, 0], pos[:, 1] = bp_data.x, bp_data.y
bp_data = pos
# Get the center of each roi
centers = []
for points in rois.values():
center_x = (points.topleft[0] + points.bottomright[0]) / 2
center_y = (points.topleft[1] + points.bottomright[1]) / 2
center = np.asarray([center_x, center_y])
roi_names = list(rois.keys())
# Calc distance toe ach roi for each frame
data_length = bp_data.shape[0]
distances = np.zeros((data_length, len(centers)))
for idx, center in enumerate(centers):
cnt = np.tile(center, data_length).reshape((data_length, 2))
dist = np.hypot(np.subtract(cnt[:, 0], bp_data[:, 0]), np.subtract(cnt[:, 1], bp_data[:, 1]))
distances[:, idx] = dist
# Get which roi the mouse is in at each frame
sel_rois = np.argmin(distances, 1)
roi_at_each_frame = tuple([roi_names[x] for x in sel_rois])
return roi_at_each_frame
def get_timeinrois_stats(data, rois, fps=None):
Quantify number of times the animal enters a roi, cumulative number of frames spend there, cumulative time in seconds
spent in the roi and average velocity while in the roi.
In which roi the mouse is at a given frame is determined with --> get_roi_at_each_frame()
Quantify the ammount of time in each roi and the avg stay in each roi
:param data: trackind data is a numpy array with shape (n_frames, 3) with data for X,Y position and Velocity
:param rois: dictionary with the position of each roi. The position is stored in a named tuple with the location of
two points defyining the roi: topleft(X,Y) and bottomright(X,Y).
:param fps: framerate at which video was acquired
:return: dictionary
# Testing
>>> position = namedtuple('position', ['topleft', 'bottomright'])
>>> rois = {'middle': position((300, 400), (500, 800))}
>>> data = np.zeros((23188, 3))
>>> res = get_timeinrois_stats(data, rois, fps=30)
>>> print(res)
def get_indexes(lst, match):
return np.asarray([i for i, x in enumerate(lst) if x == match])
# get roi at each frame of data
data_rois = get_roi_at_each_frame(data, rois)
data_time_inrois = {name: data_rois.count(name) for name in set(data_rois)} # total time (frames) in each roi
# number of enters in each roi
transitions = [n for i, n in enumerate(list(data_rois)) if i == 0 or n != list(data_rois)[i - 1]]
transitions_count = {name: transitions.count(name) for name in transitions}
# avg time spend in each roi (frames)
avg_time_in_roi = {transits[0]: time / transits[1]
for transits, time in zip(transitions_count.items(), data_time_inrois.values())}
# avg time spend in each roi (seconds)
if fps is not None:
data_time_inrois_sec = {name: t / fps for name, t in data_time_inrois.items()}
avg_time_in_roi_sec = {name: t / fps for name, t in avg_time_in_roi.items()}
data_time_inrois_sec, avg_time_in_roi_sec = None, None
# get avg velocity in each roi
avg_vel_per_roi = {}
for name in set(data_rois):
indexes = get_indexes(data_rois, name)
vels = data[indexes, 2]
avg_vel_per_roi[name] = np.average(np.asarray(vels))
results = dict(transitions_per_roi=transitions_count,
return results
if __name__ == "__main__":
import doctest
You can’t perform that action at this time.