{ "cells": [ { "cell_type": "markdown", "id": "ed042f65", "metadata": {}, "source": [ "### Change file directory and directory for output files here:" ] }, { "cell_type": "code", "execution_count": 1, "id": "aac0dc71", "metadata": {}, "outputs": [], "source": [ "# directory/folder containing files to analyze\n", "file_directory = 'C:/Users/franz/Documents/Bachelor Studium/BachelorArbeit/JupyterLabBA/files/DLCfiles/*'\n", "\n", "# directory for output files\n", "out_dir = 'C:/Users/franz/Documents/Bachelor Studium/BachelorArbeit/JupyterLabBA/files/DLCfiles_results/'\n", "\n", "# location of fps file\n", "# csv file needs to have file names in second column and fps in third column\n", "videos_fps_file = \"fps_file.csv\"\n", "\n", "# change this, if you want/ don't want to make plots\n", "# plotting takes a lot longer\n", "savePlots = False\n", "\n", "# change this to save files\n", "# final result file will always be saved\n", "saveFiles = True" ] }, { "cell_type": "markdown", "id": "b301fc8e", "metadata": {}, "source": [ "### Import packages:" ] }, { "cell_type": "code", "execution_count": 2, "id": "05a83b0e", "metadata": {}, "outputs": [], "source": [ "# import modules\n", "import matplotlib.pyplot as plt\n", "import matplotlib.colors as colors\n", "import matplotlib.cm as cm\n", "import pandas as pd\n", "import os\n", "import numpy as np\n", "import math\n", "from scipy.signal import argrelextrema\n", "import glob\n", "import matplotlib.gridspec as gridspec\n", "from scipy.interpolate import interp1d\n", "import scipy.interpolate as interpolate\n", "from IPython.core.interactiveshell import InteractiveShell\n", "\n", "%matplotlib inline\n", "plt.rcParams['figure.dpi'] = 100 # adjust fig size in notebook\n", "InteractiveShell.ast_node_interactivity = \"all\" # allows for multiple outputs per cell to be shown in notebook" ] }, { "cell_type": "markdown", "id": "9f3b2706", "metadata": {}, "source": [ "### Functions\n", "#### Data pre-processing:" ] }, { "cell_type": "code", "execution_count": 3, "id": "314f8a68", "metadata": {}, "outputs": [], "source": [ "# function to save beam points in variable for further use\n", "def beam_points(df):\n", " top_left = [np.nanmedian(df.iloc[:,45]),np.nanmedian(df.iloc[:,46])].copy()\n", " top_right = [np.nanmedian(df.iloc[:,51]),np.nanmedian(df.iloc[:,52])].copy()\n", " return(top_left,top_right)\n", "def beam_points_bottom(df):\n", " bot_left = [np.nanmedian(df.iloc[:,48]),np.nanmedian(df.iloc[:,49])].copy()\n", " bot_right = [np.nanmedian(df.iloc[:,54]),np.nanmedian(df.iloc[:,55])].copy()\n", " return(bot_left,bot_right)\n", "\n", "# function for beam height:\n", "# returns y value of upper or lower beam edge for input x value \n", "def beam(x,left,right):\n", " # y = mx + b\n", " m = (left[1]-right[1])/(left[0]-right[0])\n", " b = left[1]-m*left[0]\n", " return(m*x+b)\n", "\n", "# recalculate y values with beam edge as axis\n", "def recalc_y(df, top_left, top_right):\n", " for colnum, col in enumerate(df):\n", " if col.endswith(\"y\"):\n", " for rownum, value in enumerate(df[col]):\n", " if np.isnan(df.iloc[rownum, colnum-1]) == False and np.isnan(df.iloc[rownum, colnum]) == False:\n", " df.iloc[rownum,colnum] = value-beam(df.iloc[rownum, colnum-1], top_left, top_right)\n", " else:\n", " df.iloc[rownum,colnum] = np.NaN\n", " return(df)\n", "\n", "# convert pixel into cm\n", "def pix_to_cm(df):\n", " tl = [np.nanmedian(df.iloc[:,45]), np.nanmedian(df.iloc[:,46])]\n", " tr = [np.nanmedian(df.iloc[:,51]), np.nanmedian(df.iloc[:,52])]\n", " cm_per_pixel = 120/(np.sqrt((tr[0]-tl[0])**2+(tr[1]-tl[1])**2))\n", " cm_per_pixel\n", " for colnum, col in enumerate(df):\n", " if col.endswith(\"x\") or col.endswith(\"y\"):\n", " for rownum, row in enumerate(df[col]):\n", " df.iloc[rownum, colnum] = df.iloc[rownum, colnum]*cm_per_pixel\n", " return(df)\n", "\n", "# recalcultate x values as distance to beam beginning\n", "def recalc_x(df, tl):\n", " for colnum, col in enumerate(df):\n", " if col.endswith(\"x\"):\n", " for rownum, value in enumerate(df[col]):\n", " df.iloc[rownum, colnum] = df.iloc[rownum, colnum] - tl[0]\n", " return(df)\n" ] }, { "cell_type": "markdown", "id": "3a1cb1b0", "metadata": {}, "source": [ "#### Function for plotting all tracking points" ] }, { "cell_type": "code", "execution_count": 4, "id": "af7c3f6e", "metadata": {}, "outputs": [], "source": [ "\n", "# make plot with 19 subplots, one for each tracking point\n", "# cropped data points shown in orange\n", "def point_plot_crop(df, frames, short_file_name):\n", " if savePlots == True:\n", " fig, axs = plt.subplots(19,figsize=(8,24), sharey=True, sharex=True)\n", " fig.suptitle(\"{}\".format(short_file_name))\n", " for i in range(19):\n", " axs[i].hlines(0, xmin = beam_points(df)[0][0], xmax = beam_points(df)[1][0], color = \"k\", zorder=1)\n", " axs[i].hlines(-1.3, xmin = beam_points(df)[0][0], xmax = beam_points(df)[1][0], color = \"k\", zorder=1)\n", " axs[i].set(ylim=(-3,12))\n", " axs[i].set_ylabel(\"{}\".format(df.columns[3*i].split(\".\")[0]))\n", " for rownum in range(len(df.iloc[:,3*i])):\n", " if frames[rownum] == False:\n", " axs[i].scatter(x=df.iloc[rownum, 3*i], y=df.iloc[rownum, 3*i+1], s=2, color = \"C1\", zorder=2)\n", " if frames[rownum] == True:\n", " axs[i].scatter(x=df.iloc[rownum, 3*i], y=df.iloc[rownum, 3*i+1], s=2, color = \"C0\", zorder=2)\n", " fig.tight_layout(pad=2, h_pad=0.5, w_pad=0.5) \n", " plt.savefig(out_dir+short_file_name+\"_all_tracking_points_crop.png\")\n", " plt.close(fig)" ] }, { "cell_type": "code", "execution_count": 5, "id": "d754aa6e", "metadata": {}, "outputs": [], "source": [ "# functions for frame dropping:\n", "def beam_left(x, tl):\n", " left = False\n", " if np.isnan(x) == False and x < tl[0]:\n", " left = True\n", " return(left)\n", "def beam_right(x, tr):\n", " right = False\n", " if np.isnan(x) == False and x > tr[0]:\n", " right = True\n", " return(right)\n", "def start(df):\n", " for rownum in range(len(df)):\n", " if any(np.isnan([df.iloc[rownum, 24], df.iloc[rownum, 25], df.iloc[rownum, 39], df.iloc[rownum, 40]])) == False and -1.3 <= df.iloc[rownum, 25] <= 0.5 and -1.3 <= df.iloc[rownum, 40] <= 0.5:\n", " return(rownum)\n", " break\n", "def turn(df, rownum):\n", " turn = False\n", " if df.iloc[rownum, 0] < df.iloc[rownum, 12]:\n", " turn = True\n", " return(turn)\n", "def mouse_drop(df, rownum, bl, br):\n", " drop = False\n", " if df.iloc[rownum, 1] <= -2.3 and df.iloc[rownum, 13] <= -2.3:\n", " drop = True\n", " return(drop)\n", "def end_beam(df, rownum, tr):\n", " end_beam = False\n", " if df.iloc[rownum, 24] >= 115 or df.iloc[rownum, 39] >= 115:\n", " end_beam = True\n", " return(end_beam)\n", "def stop(df, rownum, start_frame, fps):\n", " stop = False\n", " if rownum >= start_frame and rownum > 10*round(fps) and df.iloc[rownum-10*round(fps), 24]-0.3 <= df.iloc[rownum, 24] <= df.iloc[rownum-10*round(fps), 24]+0.3 and df.iloc[rownum-10*round(fps), 39]-0.3 <= df.iloc[rownum, 39] <= df.iloc[rownum-10*round(fps), 39]+0.3:\n", " stop = True\n", " return(stop)\n", "def time_out(df, rownum, fps, start_frame):\n", " time = (rownum-start_frame)/fps\n", " if time >= 60:\n", " return(True)\n", " else:\n", " return(False)\n", "def mouse_disappear(df, rownum, fps, start_frame):\n", " disappear = False\n", " if rownum-start_frame >= 10*round(fps):\n", " if np.isnan(df.iloc[rownum, 0]) == True:\n", " visible = [True for i in range(10*round(fps))]\n", " for num, rowcount in enumerate(range(rownum, rownum-10*round(fps), -1)):\n", " if np.isnan(df.iloc[rowcount, 0]) == True:\n", " visible[num] = False\n", " if any(visible) == False:\n", " disappear = True\n", " return(disappear)\n", "\n", "\n", "def frames_to_drop(df, fps):\n", " frames = [True for i in range(len(df))]\n", " tl, tr = beam_points(df)\n", " bl, br = beam_points_bottom(df)\n", " start_frame = start(df)\n", " end_frame = None\n", " for rownum in range(len(df)):\n", " if rownum < start_frame:\n", " frames[rownum] = False\n", " continue\n", " elif rownum > start_frame:\n", " end = [turn(df, rownum), mouse_drop(df, rownum, bl=bl, br=br), end_beam(df, rownum, tr=tr), stop(df, rownum, start_frame, fps), time_out(df, rownum, fps, start_frame), mouse_disappear(df, rownum, fps, start_frame)]\n", " if any(end) == True:\n", " end_frame = rownum\n", " if end[0] == True:\n", " end_reason = \"turn\"\n", " if end[1] == True:\n", " end_reason = \"drop\"\n", " if end[2] == True:\n", " end_reason = \"end_of_beam\"\n", " if end[3] == True:\n", " end_reason = \"stop\"\n", " if end[4] == True:\n", " end_reason = \"time_over\"\n", " if end[5] == True:\n", " end_reason = \"disappear\"\n", " end_frame = rownum - 10*round(fps)\n", " for i in range(end_frame, len(frames)):\n", " frames[i] = False\n", " break\n", " if end_frame == None:\n", " end_frame = len(df)-1\n", " end_reason = \"not_specified\"\n", "\n", " if np.isnan(df.iloc[end_frame, 0]) == False and np.isnan(df.iloc[start_frame, 0]) == False:\n", " distance = df.iloc[end_frame, 0] - df.iloc[start_frame, 0]\n", " time = (end_frame-start_frame)/fps\n", " elif np.isnan(df.iloc[end_frame, 0]) == True and np.isnan(df.iloc[start_frame, 0]) == False:\n", " for i in range(end_frame, end_frame-30, -1):\n", " if np.isnan(df.iloc[i, 0]) == False and i > start_frame:\n", " distance = df.iloc[i, 0] - df.iloc[start_frame, 0]\n", " time = (i-start_frame)/fps\n", " break\n", " distance = \"not_specified\"\n", " time = (end_frame-start_frame)/fps\n", " elif np.isnan(df.iloc[end_frame, 0]) == False and np.isnan(df.iloc[start_frame, 0]) == True:\n", " for i in range(start_frame, start_frame+10):\n", " if np.isnan(df.iloc[i, 0]) == False and i > end_frame:\n", " distance = df.iloc[end_frame, 0] - df.iloc[i, 0]\n", " time = (end_frame-i)/fps\n", " break\n", " distance = \"not_specified\"\n", " time = (end_frame-start_frame)/fps\n", " elif np.isnan(df.iloc[end_frame, 0]) == True and np.isnan(df.iloc[start_frame, 0]) == True:\n", " for i in range(start_frame, start_frame+10):\n", " if np.isnan(df.iloc[i, 0]) == False and i < end_frame:\n", " new_start = i\n", " break\n", " for i in range(end_frame, end_frame-30, -1):\n", " if np.isnan(df.iloc[i, 0]) == False and i > new_start:\n", " new_end = i\n", " break\n", " if np.isnan(new_start) == False and np.isnan(new_end) == False:\n", " distance = df.iloc[new_end, 0] - df.iloc[new_start, 0]\n", " time = (new_end-new_start)/fps\n", " else:\n", " distance = \"not_specified\"\n", " time = \"not_specified\"\n", " return(frames, time, distance, end_reason)" ] }, { "cell_type": "code", "execution_count": 6, "id": "8cb3ca6c", "metadata": {}, "outputs": [], "source": [ "# data prep, loading data in pandas data frame, concat col names, invert y coordinates\n", "def prep(df, short_file_name, fps):\n", " # concat col names in pd df\n", " parts = df.iloc[0, :]\n", " coords = df.iloc[1, :]\n", " new_names = []\n", " for num, part in enumerate(parts):\n", " new_names.append(part + \".\" + coords[num])\n", " df.columns = new_names\n", " df.drop(labels=[0, 1], inplace=True)\n", " df.set_index(\"bodyparts.coords\", inplace=True)\n", " df = df.astype('float')\n", "\n", " # invert y coordinates\n", " # original video resolution: 1920 x 1080 pixels \n", " for col in df:\n", " if col.endswith(\"y\"):\n", " df[col] = 1080 - df[col]\n", "\n", " # find median beam point positions \n", " tl, tr = beam_points(df)\n", " # recalculate x and y values to match beginning off beam (x=0) and upper edge (y=0)\n", " df = recalc_y(df, tl, tr)\n", " df = recalc_x(df, tl)\n", " # conversion in cm\n", " df = pix_to_cm(df)\n", "\n", " # save pre processed file\n", " df.to_csv(out_dir+group+\"/\"+short_file_name+\"_preprocessed.csv\", index=False)\n", " \n", " # omit all values with low likelihood (below 90 %)\n", " label_quality_count = 0\n", " df_nan = df.copy()\n", " for colnum, col in enumerate(df_nan):\n", " if col.endswith(\"likelihood\"):\n", " for rownum, value in enumerate(df_nan[col]):\n", " if value < 0.9:\n", " df_nan.iloc[rownum, [colnum-2, colnum-1]] = np.NaN\n", " label_quality_count += 1\n", " df_nan = df_nan.astype('float')\n", " \n", " # drop frames before start and after end of analysis\n", " frames, time, distance, end_reason = frames_to_drop(df_nan, fps)\n", " df_nan_crop = df_nan.iloc[frames, :].copy()\n", " df_crop = df.iloc[frames, :].copy()\n", " df_crop.to_csv(out_dir+group+\"/\"+short_file_name+\"_preprocessed_crop.csv\", index=False)\n", " # plot all tracking points in cm\n", " point_plot_crop(df_nan, frames, short_file_name)\n", "\n", " if distance == \"not_specified\":\n", " speed = \"not_specified\"\n", " else:\n", " speed = distance/time\n", "\n", " return(df_nan_crop, end_reason, time, distance, speed)" ] }, { "cell_type": "markdown", "id": "cd8806b2", "metadata": {}, "source": [ "#### Angle calculations:" ] }, { "cell_type": "code", "execution_count": 7, "id": "8e60d87c", "metadata": {}, "outputs": [], "source": [ "def ang_velocity(angles, fps, df):\n", " v = []\n", " for rownum in range(len(angles)-1):\n", " if np.isnan(angles[rownum+1]) == False and np.isnan(angles[rownum]) == False:\n", " delta = angles[rownum+1] - angles[rownum]\n", " v.append(delta*fps)\n", " else:\n", " v.append(np.NAN)\n", " return(v)\n", "\n", "def calculate_angle_features(df, short_file_name, angle_name, fps):\n", " if angle_name == \"ankle\":\n", " point1 = [df.iloc[:,33],df.iloc[:,34]] #knee\n", " point2 = [df.iloc[:,36],df.iloc[:,37]] #ankle\n", " point3 = [df.iloc[:,39],df.iloc[:,40]] #paw\n", " point_position = df.iloc[:, 9]-df.iloc[:,36] # (x value middle back) - (x value ankle)\n", " point_height = df.iloc[:,37]\n", " elif angle_name == \"knee\":\n", " point1 = [df.iloc[:,30],df.iloc[:,31]] #hip\n", " point2 = [df.iloc[:,33],df.iloc[:,34]] #knee\n", " point3 = [df.iloc[:,36],df.iloc[:,37]] #ankle\n", " point_position = df.iloc[:, 9]-df.iloc[:,33] # (x value middle back) - (x value knee)\n", " point_height = df.iloc[:,34]\n", " elif angle_name == \"hip\":\n", " point1 = [df.iloc[:, 9],df.iloc[:,10]] #middle back\n", " point2 = [df.iloc[:,30],df.iloc[:,31]] #hip\n", " point3 = [df.iloc[:,33],df.iloc[:,34]] #knee\n", " point_position = df.iloc[:, 9]-df.iloc[:,30] # (x value middle back) - (x value hip)\n", " point_height = df.iloc[:,31]\n", " elif angle_name == \"wrist\":\n", " point1 = [df.iloc[:,18],df.iloc[:,19]] #elbow\n", " point2 = [df.iloc[:,21],df.iloc[:,22]] #wrist\n", " point3 = [df.iloc[:,24],df.iloc[:,25]] #paw\n", " point_position = df.iloc[:, 6]-df.iloc[:,21] # (x value upper back) - (x value wrist)\n", " point_height = df.iloc[:,22]\n", " elif angle_name == \"elbow\":\n", " point1 = [df.iloc[:,15],df.iloc[:,16]] #shoulder\n", " point2 = [df.iloc[:,18],df.iloc[:,19]] #elbow\n", " point3 = [df.iloc[:,21],df.iloc[:,22]] #wrist\n", " point_position = df.iloc[:, 6]-df.iloc[:,18] # (x value upper back) - (x value elbow)\n", " point_height = df.iloc[:,19]\n", " elif angle_name == \"shoulder\":\n", " point1 = [df.iloc[:, 6],df.iloc[:, 7]] #upper back\n", " point2 = [df.iloc[:,15],df.iloc[:,16]] #shoulder\n", " point3 = [df.iloc[:,18],df.iloc[:,19]] #elbow\n", " point_position = df.iloc[:, 6]-df.iloc[:,15] # (x value upper back) - (x value shoulder)\n", " point_height = df.iloc[:,16]\n", "\n", " angles = []\n", " for row_num in range(len(df)):\n", " if any(np.isnan([point1[0][row_num], point1[1][row_num], point2[0][row_num], point2[1][row_num], point3[0][row_num], point3[1][row_num]])) == False:\n", " a = np.sqrt((point1[0][row_num] - point2[0][row_num])**2 + (point1[1][row_num] - point2[1][row_num])**2) # distance point1 -> point2\n", " b = np.sqrt((point2[0][row_num] - point3[0][row_num])**2 + (point2[1][row_num] - point3[1][row_num])**2) # distance point2 -> point3\n", " c = np.sqrt((point1[0][row_num] - point3[0][row_num])**2 + (point1[1][row_num] - point3[1][row_num])**2) # distance point1 -> point3\n", " angle = math.acos((a**2+b**2-c**2)/(2*a*b))\n", " angle = math.degrees(angle)\n", " if angle_name == \"wrist\":\n", " if df.iloc[row_num, 24] < df.iloc[row_num, 21]:\n", " angle = 360 - angle\n", " if angle_name == \"elbow\":\n", " if df.iloc[row_num, 21] < df.iloc[row_num, 18]:\n", " angle = 360 - angle\n", " if angle_name == \"shoulder\":\n", " if df.iloc[row_num, 18] < df.iloc[row_num, 15]:\n", " angle = 360 - angle\n", " angles.append(angle)\n", " else:\n", " angles.append(np.NAN)\n", "\n", " angle_min = np.nanmin(angles)\n", " angle_max = np.nanmax(angles)\n", " angle_mean = np.nanmean(angles)\n", " angle_median = np.nanmedian(angles)\n", " angle_std = np.nanstd(angles)\n", " angle_ROM = angle_max-angle_min\n", "\n", " velocities = ang_velocity(angles, fps, df)\n", " ang_velocity_min = np.nanmin(velocities)\n", " ang_velocity_max = np.nanmax(velocities)\n", " ang_velocity_mean = np.nanmean(velocities)\n", " ang_velocity_std = np.nanstd(velocities)\n", "\n", "# if savePlots == True:\n", "# plt.plot(angles)\n", "# plt.title(\"{}: {} angles\".format(short_file_name, angle_name))\n", "# plt.xlabel(\"frames\")\n", "# plt.ylabel(\"degrees [°]\")\n", "# plt.savefig(out_dir+short_file_name+\"_\"+angle_name+\"_angles.png\")\n", "# plt.close()\n", "\n", "# plt.plot(velocities)\n", "# plt.xlabel(\"frames\")\n", "# plt.ylabel(\"degrees per second [°/sec]\")\n", "# plt.title(\"{}: {} angular velocity\".format(short_file_name, angle_name))\n", "# plt.savefig(out_dir+short_file_name+\"_\"+angle_name+\"_angvelocities.png\")\n", "# plt.close()\n", "\n", "# if len(angles) == 0:\n", "# ang_velocity_min = np.NAN\n", "# ang_velocity_max = np.NAN\n", "# ang_velocity_mean = np.NAN\n", "# ang_velocity_std = np.NAN\n", "# angle_min = np.NAN\n", "# angle_max = np.NAN\n", "# angle_mean = np.NAN\n", "# angle_median = np.NAN\n", "# angle_std = np.NAN\n", "# angle_ROM = np.NAN\n", "\n", " height_min = np.nanmin(point_height)\n", " height_max = np.nanmax(point_height)\n", " height_mean = np.nanmean(point_height)\n", " height_median = np.nanmedian(point_height)\n", " height_std = np.nanstd(point_height)\n", "\n", " if angle_name in [\"ankle\", \"knee\", \"hip\"]:\n", " ref = df.iloc[:, 9]\n", " if angle_name in [\"wrist\", \"elbow\", \"shoulder\"]:\n", " ref = df.iloc[:, 6]\n", "\n", " positions = []\n", " for i in range(len(df)):\n", " positions.append(point2[0][i]-ref[i])\n", " position_min = np.nanmin(positions)\n", " position_max = np.nanmax(positions)\n", " position_mean = np.nanmean(positions)\n", " position_std = np.nanstd(positions) \n", "\n", " return(angles, height_min, height_max, height_mean, height_median, height_std, angle_min, angle_max, angle_mean, angle_median, angle_ROM, angle_std, ang_velocity_min, ang_velocity_max, ang_velocity_mean, ang_velocity_std, position_min, position_max, position_mean, position_std)\n", "\n", "def angle_cycle(angles, st_begin, sw_begin):\n", " init_angle = []\n", " presw_angle = []\n", " if len(angles) > 0:\n", " for num in st_begin:\n", " init_angle.append(angles[num])\n", " for num in sw_begin:\n", " presw_angle.append(angles[num])\n", " if len(init_angle) > 0 and len(presw_angle) > 0:\n", " out = [np.nanmin(init_angle), np.nanmax(init_angle), np.nanmean(init_angle), np.nanmedian(init_angle), np.nanstd(init_angle), np.nanmin(presw_angle), np.nanmax(presw_angle), np.nanmean(presw_angle), np.nanmedian(presw_angle), np.nanstd(presw_angle)]\n", " else:\n", " out = [np.NAN, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN, np.NAN]\n", " return(out)\n", "\n", "def feature_calc(df, st_fp, sw_fp, st_hp, sw_hp, short_file_name, fps):\n", " res = []\n", " angle_df = pd.DataFrame()\n", " for name in [\"ankle\", \"knee\", \"hip\", \"wrist\", \"elbow\", \"shoulder\"]:\n", " angles, height_min, height_max, height_mean, height_median, height_std, angle_min, angle_max, angle_mean, angle_median, angle_ROM, angle_std, ang_velocity_min, ang_velocity_max, ang_velocity_mean, ang_velocity_std, position_min, position_max, position_mean, position_std = calculate_angle_features(df, short_file_name, name, fps)\n", " res.extend([height_min, height_max, height_mean, height_median, height_std, angle_min, angle_max, angle_mean, angle_median, angle_ROM, angle_std, ang_velocity_min, ang_velocity_max, ang_velocity_mean, ang_velocity_std, position_min, position_max, position_mean, position_std])\n", " if name in [\"ankle\", \"knee\", \"hip\"]:\n", " res.extend(angle_cycle(angles, st_hp, sw_hp))\n", " if name in [\"wrist\", \"elbow\", \"shoulder\"]:\n", " res.extend(angle_cycle(angles, st_fp, sw_fp))\n", " angle_df[name+\"_angle\"] = angles\n", " if saveFiles == True:\n", " angle_df.to_csv(out_dir+group+\"/\"+short_file_name+\"_angles.csv\", index=False)\n", " return(res)" ] }, { "cell_type": "markdown", "id": "4f30a28b", "metadata": {}, "source": [ "#### Step cycles:" ] }, { "cell_type": "code", "execution_count": 8, "id": "927cd51b", "metadata": {}, "outputs": [], "source": [ "def find_cycles_forepaw(df):\n", " # list of index numbers of df, doesn't start with 0, bc frames were cropped\n", " index = range(len(df))\n", " # replace NAN by previous values\n", " y = df.iloc[:, 25].values\n", " for num, val in enumerate(y):\n", " if np.isnan(val) == True:\n", " y[num] = y[num-1]\n", " x = df.iloc[:, 24].copy()\n", " for num, val in enumerate(x):\n", " if np.isnan(val) == True:\n", " x[num] = x[num-1]\n", " # cubic interpolation\n", " f = interpolate.interp1d(range(len(df)), y, kind='cubic')\n", " index_new = np.linspace(0, len(df)-1, num=1000, endpoint=True)\n", " ynew = f(index_new)\n", " if all(np.isnan(ynew)) == True:\n", " f = interpolate.interp1d(range(len(df)), y, kind='linear')\n", " ynew = f(index_new)\n", "\n", " # find minima\n", " minima_int = argrelextrema(data=ynew, comparator=np.less, mode='clip', order=20)\n", " \n", " # recalculate, round minima to match frame numbers\n", " minima_round = []\n", " for num in minima_int[0]:\n", " val = int(round(index_new[num]))\n", " minima_round.append(val)\n", "\n", " #plot with subplots\n", " if savePlots == True:\n", " fig, axs = plt.subplots(1,4,figsize=(20,6),sharey=False,sharex=False)\n", " fig.suptitle(short_file_name+\" INTERPOLATED\") \n", " for i in range(len(index_new)):\n", " axs[0].scatter(index_new[i],ynew[i], color=\"C0\",s=2, zorder = 2)\n", " for rownum in range(len(ynew)):\n", " if rownum in minima_int[0]:\n", " axs[0].scatter(index_new[rownum], ynew[rownum], color = \"C1\", zorder = 3)\n", " for rownum in range(len(df)):\n", " if rownum in minima_round:\n", " axs[2].scatter(rownum, x[rownum], color = \"C1\")\n", " axs[1].scatter(rownum, y[rownum], color = \"C1\")\n", " axs[2].plot(x)\n", " axs[0].set_ylabel(\"y values right forepaw interpolated [cm]\")\n", " axs[0].set_xlabel(\"frame indices\")\n", " axs[2].set_xticks(axs[2].get_xticks()[::25])\n", " axs[2].set_ylabel(\"x values right forepaw [cm]\")\n", " axs[2].set_xlabel(\"frame indices\")\n", " axs[1].plot(y)\n", " axs[1].set_xticks(axs[1].get_xticks()[::25])\n", " axs[1].set_ylabel(\"y values right forepaw [cm]\")\n", " axs[1].set_xlabel(\"frame indices\")\n", " axs[3].plot(x,y, color = \"C0\", zorder = 0)\n", " axs[3].scatter(x,y, color = \"C0\", zorder = 1)\n", " axs[3].set_ylabel(\"y values right forepaw [cm]\")\n", " axs[3].set_xlabel(\"x values right forepaw [cm]\")\n", " for rownum in range(len(df)):\n", " if rownum in minima_round:\n", " axs[3].scatter(x=x[rownum], y=y[rownum], color = \"C1\", zorder = 2) \n", " plt.tight_layout()\n", " plt.savefig(out_dir+short_file_name+\"_forepaw_cycles.png\")\n", " plt.close(fig)\n", " \n", "\n", "\n", " stance = [False for i in range(len(df))]\n", " for i in minima_round:\n", " for j in range(i, i-10, -1):\n", " if j >= index[0]:\n", " if df.iloc[i, 25]-0.06 <= df.iloc[j, 25] <= df.iloc[i, 25]+0.06:\n", " stance[j] = True\n", " else:\n", " break\n", " for j in range(i, i+10):\n", " if j <= index[-1]:\n", " if df.iloc[i, 25]-0.06 <= df.iloc[j, 25] <= df.iloc[i, 25]+0.06:\n", " stance[j] = True\n", " else:\n", " break\n", " # find step count, begin and length of swing and stance phases\n", " step_count = -1\n", " st_count = 0\n", " sw_count = 0\n", " cycle_dur = []\n", " stance_dur = []\n", " swing_dur = []\n", " stance_begin = []\n", " swing_begin = []\n", " for num, val in enumerate(stance):\n", " if num >= 1:\n", " if val == True and stance[num-1] == False:\n", " step_count += 1\n", " if len(stance_begin) > 0:\n", " stance_dur.append(st_count)\n", " swing_dur.append(sw_count)\n", " cycle_dur.append(sw_count+st_count)\n", " st_count = 1\n", " stance_begin.append(num)\n", " if val == True and stance[num-1] == True:\n", " st_count += 1\n", " if val == False and stance[num-1] == True:\n", " sw_count = 1\n", " swing_begin.append(num)\n", " if val == False and stance[num-1] == False:\n", " sw_count += 1\n", " ratio = []\n", " stride_length = []\n", " for i in range(len(stance_dur)):\n", " cycle_dur[i] = cycle_dur[i]/fps\n", " stance_dur[i] = stance_dur[i]/fps\n", " swing_dur[i] = swing_dur[i]/fps\n", " ratio.append(swing_dur[i]/stance_dur[i])\n", " if i > 0:\n", " stride_length.append(df.iloc[i, 24] - df.iloc[i-1, 24])\n", " \n", " #plot forepaw height in stance and swing phase per frame\n", " if savePlots == True:\n", " plt.plot(index, y, color = \"C0\", zorder = 0)\n", " plt.scatter(index, y, color = \"C0\", zorder = 1)\n", " plt.ylabel(\"y values right forepaw [cm]\")\n", " plt.xlabel(\"frame indices\")\n", " plt.title(\"{}: forepaw step cycles\".format(short_file_name))\n", " for rownum in range(len(df)):\n", " if stance[rownum] == True:\n", " plt.scatter(index[rownum], y[rownum], color = \"C2\", zorder = 2)\n", " if rownum in minima_round:\n", " plt.scatter(index[rownum], y[rownum], color = \"C1\", zorder = 3)\n", " plt.savefig(out_dir+short_file_name+\"_forepaw_cycles_step_phase_per_frame.png\")\n", " plt.close()\n", " \n", " # plot forepaw height in stance and swing phase against x \n", " plt.plot(x,y, color = \"C0\", zorder = 0)\n", " plt.scatter(x,y, color = \"C0\", zorder = 1)\n", " plt.ylabel(\"y values right forepaw [cm]\")\n", " plt.xlabel(\"x values right forepaw [cm]\")\n", " plt.title(\"{}: forepaw step cycles\".format(short_file_name))\n", " for rownum in range(len(df)):\n", " if stance[rownum] == True:\n", " plt.scatter(x=x[rownum], y=y[rownum], color = \"C2\", zorder = 2)\n", " if rownum in minima_round:\n", " plt.scatter(x=x[rownum], y=y[rownum], color = \"C1\", zorder = 3) \n", " plt.savefig(out_dir+short_file_name+\"_forepaw_cycles_step_phase.png\")\n", " plt.close()\n", " \n", " return(np.nanmedian(cycle_dur), np.nanmedian(stance_dur), np.nanmedian(swing_dur), np.nanmedian(ratio), np.nanmedian(stride_length), stance_begin, swing_begin)" ] }, { "cell_type": "code", "execution_count": 9, "id": "9839cf8c", "metadata": {}, "outputs": [], "source": [ "def find_cycles_hindpaw(df):\n", " # list of index numbers of df, doesn't start with 0, bc frames were cropped\n", " index = range(len(df))\n", " # replace NAN by previous values\n", " y = df.iloc[:, 40].copy()\n", " for num, val in enumerate(y):\n", " if np.isnan(val) == True:\n", " y[num] = y[num-1]\n", " x = df.iloc[:, 39].copy()\n", " for num, val in enumerate(x):\n", " if np.isnan(val) == True:\n", " x[num] = x[num-1]\n", " # cubic interpolation\n", " f = interp1d(index, y, kind='cubic')\n", " index_new = np.linspace(0, len(df)-1, num=1000, endpoint=True)\n", " ynew = f(index_new)\n", " if all(np.isnan(ynew)) == True:\n", " f = interpolate.interp1d(index, y, kind='linear')\n", " ynew = f(index_new)\n", " # find minima\n", " minima_int = argrelextrema(data=ynew, comparator=np.less, mode='clip', order=20)\n", " \n", " # recalculate, round minima to match frame numbers\n", " minima_round = []\n", " for num in minima_int[0]:\n", " val = int(round(index_new[num]))\n", " minima_round.append(val)\n", " \n", " #plot\n", " if savePlots == True:\n", " fig, axs = plt.subplots(1,4,figsize=(20,6),sharey=False,sharex=False)\n", " fig.suptitle(short_file_name+\" INTERPOLATED\") \n", " for i in range(len(index_new)):\n", " axs[0].scatter(index_new[i], ynew[i], color=\"C0\",s=2, zorder = 2)\n", " for rownum in range(len(ynew)):\n", " if rownum in minima_int[0]:\n", " axs[0].scatter(index_new[rownum], ynew[rownum], color = \"C1\", zorder = 3)\n", " for rownum in range(len(df)):\n", " if rownum in minima_round:\n", " axs[2].scatter(rownum, x[rownum], color = \"C1\")\n", " axs[1].scatter(rownum, y[rownum], color = \"C1\")\n", " axs[2].plot(x)\n", " axs[0].set_ylabel(\"y values right hindpaw interpolated [cm]\")\n", " axs[0].set_xlabel(\"frame indices\")\n", " axs[2].set_xticks(axs[2].get_xticks()[::25])\n", " axs[2].set_ylabel(\"x values right hindpaw [cm]\")\n", " axs[2].set_xlabel(\"frame indices\")\n", " axs[1].plot(y)\n", " axs[1].set_xticks(axs[1].get_xticks()[::25])\n", " axs[1].set_ylabel(\"y values right hindpaw [cm]\")\n", " axs[1].set_xlabel(\"frame indices\")\n", " axs[3].plot(x, y, color = \"C0\", zorder = 0)\n", " axs[3].scatter(x, y, color = \"C0\", zorder = 1)\n", " axs[3].set_ylabel(\"y values right hindpaw [cm]\")\n", " axs[3].set_xlabel(\"x values right hindpaw [cm]\")\n", " for rownum in range(len(df)):\n", " if rownum in minima_round:\n", " axs[3].scatter(x=x[rownum], y=y[rownum], color = \"C1\", zorder = 2)\n", " plt.tight_layout()\n", " plt.savefig(out_dir+short_file_name+\"_hindpaw_cycles.png\")\n", " plt.close(fig)\n", "\n", " # find stance phases\n", " stance = [False for i in range(len(df))]\n", " for i in minima_round:\n", " for j in range(i, i-10, -1):\n", " if j >= index[0]:\n", " if df.iloc[i, 40] - 0.06 <= df.iloc[j, 40] <= df.iloc[i, 40] + 0.06:\n", " stance[j] = True\n", " else: \n", " break\n", " for j in range(i, i+10):\n", " if j <= index[-1]:\n", " if df.iloc[i, 40] - 0.06 <= df.iloc[j, 40] <= df.iloc[i, 40] + 0.06:\n", " stance[j] = True\n", " else:\n", " break\n", "\n", " # find step count, begin and length of swing and stance phases\n", " step_count = -1\n", " st_count = 0\n", " sw_count = 0\n", " cycle_dur = []\n", " stance_dur = []\n", " swing_dur = []\n", " stance_begin = []\n", " swing_begin = []\n", " for num, val in enumerate(stance):\n", " if num >= 1:\n", " if val == True and stance[num-1] == False:\n", " step_count += 1\n", " if len(stance_begin) > 0:\n", " stance_dur.append(st_count)\n", " swing_dur.append(sw_count)\n", " cycle_dur.append(sw_count+st_count)\n", " st_count = 1\n", " stance_begin.append(num)\n", " if val == True and stance[num-1] == True:\n", " st_count += 1\n", " if val == False and stance[num-1] == True:\n", " sw_count = 1\n", " swing_begin.append(num)\n", " if val == False and stance[num-1] == False:\n", " sw_count += 1\n", " ratio = []\n", " stride_length = []\n", " for i in range(len(stance_dur)):\n", " cycle_dur[i] = cycle_dur[i]/fps\n", " stance_dur[i] = stance_dur[i]/fps\n", " swing_dur[i] = swing_dur[i]/fps\n", " ratio.append(swing_dur[i]/stance_dur[i])\n", " if i > 0:\n", " stride_length.append(df.iloc[i, 39]-df.iloc[i-1, 39])\n", " # plot\n", " if savePlots == True:\n", " plt.plot(index, y, color = \"C0\", zorder = 0)\n", " plt.scatter(index, y, color = \"C0\", zorder = 1)\n", " plt.ylabel(\"y values right hindpaw [cm]\")\n", " plt.xlabel(\"frame indices\")\n", " for rownum in range(len(df)):\n", " if stance[rownum] == True:\n", " plt.scatter(index[rownum], y[rownum], color = \"C2\", zorder = 2)\n", " if rownum in minima_round:\n", " plt.scatter(index[rownum], y[rownum], color = \"C1\", zorder = 3)\n", " plt.savefig(out_dir+short_file_name+\"_hindpaw_cycles_step_phase_per_frame.png\")\n", " plt.close()\n", " \n", " # plot\n", " plt.plot(x,y, color = \"C0\", zorder = 0)\n", " plt.scatter(x,y, color = \"C0\", zorder = 1)\n", " plt.ylabel(\"y values right hindpaw [cm]\")\n", " plt.xlabel(\"x values right hindpaw [cm]\")\n", " for rownum in range(len(df)):\n", " if stance[rownum] == True:\n", " plt.scatter(x=x[rownum], y=y[rownum], color = \"C2\", zorder = 2)\n", " if rownum in minima_round:\n", " plt.scatter(x=x[rownum], y=y[rownum], color = \"C1\", zorder = 3) \n", " plt.savefig(out_dir+short_file_name+\"_hindpaw_cycles_step_phase.png\")\n", " plt.close()\n", " \n", " return(np.nanmean(cycle_dur), np.nanmean(stance_dur), np.nanmean(swing_dur), np.nanmean(ratio), np.nanmean(stride_length), stance_begin, swing_begin, minima_round)" ] }, { "cell_type": "markdown", "id": "ccc123c8", "metadata": {}, "source": [ "#### Function for plotting skeleton:" ] }, { "cell_type": "code", "execution_count": 10, "id": "e6445aba", "metadata": {}, "outputs": [], "source": [ "# function to plot skeleton of all tracking points for a given frame range\n", "# for showing one frame: framestart = x, frameend = x\n", "\n", "def plot_tracking_points (df, framestart, frameend, figname, show_labels=False):\n", " if savePlots == True:\n", " # make labels and arrays for coordinates\n", " colors = cm.rainbow(np.linspace(0, 1, frameend+1-framestart))\n", "\n", " for frame in range(framestart, frameend+1):\n", " if frame in range(len(df)):\n", " labels = []\n", " plot_data_x = []\n", " plot_data_y = []\n", " for colnum, colname in enumerate(df.columns):\n", " if colname.endswith(\"x\"):\n", " plot_data_x.append(df.iloc[frame,colnum])\n", " labels.append(colname.split(\".\")[0])\n", " if colname.endswith(\"y\"):\n", " plot_data_y.append(df.iloc[frame,colnum])\n", "\n", " c = colors[framestart-frame]\n", " plt.plot(plot_data_x[0:2],plot_data_y[0:2], color = c); #snout,neck\n", " plt.plot(plot_data_x[1:3],plot_data_y[1:3], color = c); #neck,upper_back\n", " plt.plot(plot_data_x[2:4],plot_data_y[2:4], color = c); #upper_back,middle_back\n", " plt.plot(plot_data_x[3:5],plot_data_y[3:5], color = c); #middle_back,tailbase\n", " plt.plot([plot_data_x[1],plot_data_x[5]],[plot_data_y[1],plot_data_y[5]], color = c); #neck, shoulder_right\n", " plt.plot([plot_data_x[2],plot_data_x[5]],[plot_data_y[2],plot_data_y[5]], color = c); #upper_back,shoulder_right\n", " plt.plot(plot_data_x[5:7],plot_data_y[5:7], color = c); #shoulder_right,elbow_right\n", " plt.plot(plot_data_x[6:8],plot_data_y[6:8], color = c); #elbow_right,wrist_right\n", " plt.plot(plot_data_x[7:9],plot_data_y[7:9], color = c); #wrist_right,forepaw_right\n", " plt.plot([plot_data_x[3],plot_data_x[10]],[plot_data_y[3],plot_data_y[10]], color = c); #middle_back, hip_right\n", " plt.plot([plot_data_x[4],plot_data_x[10]],[plot_data_y[4],plot_data_y[10]], color = c); #tailbase,hip_right\n", " plt.plot(plot_data_x[10:12],plot_data_y[10:12], color = c); #hip_right,knee_right\n", " plt.plot(plot_data_x[11:13],plot_data_y[11:13], color = c); #knee_right,ankle_right\n", " plt.plot(plot_data_x[12:14],plot_data_y[12:14], color = c); #ankle_right,hindpaw_right \n", "\n", " plt.plot([plot_data_x[5],plot_data_x[10]],[plot_data_y[5],plot_data_y[10]], color = c); #hip_right, shoulder_right\n", " plt.plot([plot_data_x[0],plot_data_x[5]],[plot_data_y[0],plot_data_y[5]], color = c); #snout, shoulder_right\n", " plt.plot([plot_data_x[4],plot_data_x[11]],[plot_data_y[4],plot_data_y[11]], color = c); #tailbase, knee_right\n", "\n", " plt.plot([plot_data_x[15],plot_data_x[17]],[plot_data_y[15],plot_data_y[17]], color = c); #beam_left_top, beam_right_top\n", " plt.plot(plot_data_x[15:17],plot_data_y[15:17], color = c); #beam_left_top, beam_left_bottom\n", " plt.plot([plot_data_x[16],plot_data_x[18]],[plot_data_y[16],plot_data_y[18]], color = c); #beam_left_bottom, beam_right_bottom\n", " plt.plot(plot_data_x[17:],plot_data_y[17:], color = c); #beam_right_top, beam_right_bottom\n", "\n", " for i in range(len(plot_data_x)):\n", " if i != 9 and i != 14:\n", " plt.scatter(plot_data_x[i], plot_data_y[i], s=4, color=c) # scatter tracking points\n", "\n", " if show_labels == True:\n", " count = 0\n", " for x, y in zip(plot_data_x[0:15], plot_data_y[0:15]):\n", " plt.annotate(text=labels[count],\n", " xy=(x, y),\n", " textcoords=\"offset points\",\n", " xytext=(0, 5),\n", " ha='center',\n", " fontsize=8)\n", " count += 1\n", "\n", " if frame == framestart:\n", " if np.isnan(plot_data_x[3]) == False:\n", " xlim_left = plot_data_x[3]-5\n", " elif np.isnan(plot_data_x[4]) == False:\n", " xlim_left = plot_data_x[4]-4\n", " elif np.isnan(plot_data_x[13]) == False:\n", " xlim_left = plot_data_x[13]-4\n", " elif np.isnan(plot_data_x[0]) == False:\n", " xlim_left = plot_data_x[0]-7\n", " else:\n", " xlim_left = None\n", " if frame == frameend:\n", " xlim_right = plot_data_x[3]+7\n", " if np.isnan(plot_data_x[3]) == False:\n", " xlim_right = plot_data_x[3]+7\n", " elif np.isnan(plot_data_x[0]) == False:\n", " xlim_right = plot_data_x[0]+4\n", " elif np.isnan(plot_data_x[1]) == False:\n", " xlim_right = plot_data_x[1]+4\n", " elif np.isnan(plot_data_x[8]) == False:\n", " xlim_right = plot_data_x[8]+4\n", " else:\n", " xlim_right = None\n", "\n", " plt.title(\"file: {}, frame: {} - {}\".format(short_file_name, framestart, frameend))\n", " if xlim_left != None and xlim_right != None:\n", " plt.xlim(xlim_left, xlim_right)\n", " plt.ylim(-2, (xlim_right-xlim_left) / 6)\n", "\n", " plt.savefig(out_dir + short_file_name + figname + \"skeleton_plot.png\")\n", " plt.close()\n", " " ] }, { "cell_type": "markdown", "id": "25ebb9bc", "metadata": {}, "source": [ "#### Hindlimb drops:" ] }, { "cell_type": "code", "execution_count": 11, "id": "70d941c2", "metadata": {}, "outputs": [], "source": [ "def dist(point1, point2):\n", " return(np.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2))\n", "\n", "def angle_calc (a,b,c):\n", " return(math.degrees(math.acos((a**2+b**2-c**2)/(2*a*b))))\n", "\n", "def hindlimb_drops(df,distance, minima_hp):\n", " drop = [False for i in range(len(df))]\n", " for rownum, yval in enumerate(df.iloc[:,40]):\n", " if yval < -1.3 and rownum >= 10:\n", " drop[rownum] = True\n", " drop_count = 0\n", " drop_frames = []\n", " for num,val in enumerate(drop):\n", " if val == True and drop[num-1] == False:\n", " drop_count += 1\n", " drop_frames.append(num)\n", " for frame in drop_frames:\n", " plot_tracking_points(df, frame-10,frame+10,\"_hindlimbdrop_{}_\".format(frame))\n", "\n", " # hindlimb drop characteristics\n", " paw_h = []\n", " ank_ang = []\n", " knee_ang = []\n", " hip_ang = []\n", " tilt_low = []\n", " tilt_up = []\n", " midb_h = []\n", " tb_h = []\n", " minimum = None\n", " for frame in drop_frames:\n", " for i in range(frame, frame+10):\n", " if i in minima_hp:\n", " minimum = i\n", " break\n", " if minimum != None:\n", " paw_h.append(df.iloc[minimum,40]) # paw height\n", " midb_h.append(df.iloc[minimum,10]) # midback height\n", " tb_h.append(df.iloc[minimum,13]) # tailbase height\n", " paw = [df.iloc[minimum,39],df.iloc[minimum,40]]\n", " ankle = [df.iloc[minimum,36],df.iloc[minimum,37]]\n", " knee = [df.iloc[minimum,33],df.iloc[minimum,34]]\n", " hip = [df.iloc[minimum,30],df.iloc[minimum,31]]\n", " midb = [df.iloc[minimum, 9],df.iloc[minimum,10]]\n", " if np.any(np.isnan([paw, ankle, knee]))==False:\n", " ank_ang.append(angle_calc(dist(knee,ankle), dist(ankle,paw), dist(knee,paw))) # ankle angle\n", " if np.any(np.isnan([ankle, knee, hip]))==False:\n", " knee_ang.append(angle_calc(dist(hip,knee), dist(knee,ankle), dist(hip,ankle))) # knee angle\n", " if np.any(np.isnan([knee, hip, midb]))==False:\n", " hip_ang.append(angle_calc(dist(midb,hip), dist(hip,knee), dist(midb,knee))) # hip angle \n", " if np.any(np.isnan([df.iloc[minimum,7],df.iloc[minimum,10],df.iloc[minimum,6],df.iloc[minimum,9]]))==False:\n", " tilt_up.append((df.iloc[minimum,7]-df.iloc[minimum,10])/(df.iloc[minimum,6]-df.iloc[minimum,9])) #slope between upper and middle back\n", " if np.any(np.isnan([df.iloc[minimum,13],df.iloc[minimum,10],df.iloc[minimum,12],df.iloc[minimum,9]]))==False:\n", " tilt_low.append((df.iloc[minimum,13]-df.iloc[minimum,10])/(df.iloc[minimum,12]-df.iloc[minimum,9])) #slope between tailbase and middle back\n", "\n", " if distance == \"not_specified\":\n", " if drop_count == 0:\n", " drop_rel = 0\n", " else:\n", " drop_rel = \"not_specified\"\n", " else:\n", " drop_rel = drop_count/distance*100\n", " return(drop_count, drop_rel, np.nanmean(paw_h),np.nanmean(ank_ang),np.nanmean(knee_ang),np.nanmean(hip_ang), np.nanmean(tilt_up), np.nanmean(tilt_low), np.nanmean(midb_h), np.nanmean(tb_h))" ] }, { "cell_type": "markdown", "id": "8be6ef47", "metadata": {}, "source": [ "#### Other paw features:" ] }, { "cell_type": "code", "execution_count": 12, "id": "b391dcaa", "metadata": {}, "outputs": [], "source": [ "def paw_distance(df):\n", " dist = []\n", " for rownum in range(len(df)):\n", " dist.append(df.iloc[rownum, 24] - df.iloc[rownum, 39])\n", " return(np.nanmin(dist), np.nanmax(dist), np.nanmean(dist), np.nanstd(dist))" ] }, { "cell_type": "code", "execution_count": 13, "id": "7053b24e", "metadata": {}, "outputs": [], "source": [ "def paw_velocity(df):\n", " v_fore = []\n", " for rownum in range(len(df)-1):\n", " if any(np.isnan([df.iloc[rownum+1, 24], df.iloc[rownum, 24], df.iloc[rownum+1, 25], df.iloc[rownum, 25]])) == False:\n", " d = np.sqrt((df.iloc[rownum+1, 24] - df.iloc[rownum, 24])**2 + (df.iloc[rownum+1, 25] - df.iloc[rownum, 25])**2)\n", " t = 1 / fps\n", " v_fore.append(d / t)\n", " else:\n", " v_fore.append(np.NAN)\n", " v_hind = []\n", " for rownum in range(len(df)-1):\n", " if any(np.isnan([df.iloc[rownum+1, 39], df.iloc[rownum, 39], df.iloc[rownum+1, 40], df.iloc[rownum, 40]])) == False:\n", " d = np.sqrt((df.iloc[rownum+1, 39] - df.iloc[rownum, 39])**2 + (df.iloc[rownum+1, 40] - df.iloc[rownum, 40])**2)\n", " t = 1/fps\n", " v_hind.append(d/t)\n", " else:\n", " v_hind.append(np.NAN)\n", "\n", " return(np.nanmin(v_fore), np.nanmax(v_fore), np.nanmean(v_fore), np.nanstd(v_fore), np.nanmin(v_hind), np.nanmax(v_hind), np.nanmean(v_hind), np.nanstd(v_hind))" ] }, { "cell_type": "markdown", "id": "3e5c26c1", "metadata": {}, "source": [ "#### Tilt of upper and lower trunk:" ] }, { "cell_type": "code", "execution_count": 14, "id": "694f2ffe", "metadata": {}, "outputs": [], "source": [ "def trunk_tilt(df):\n", " tilt_up = [] # slope between upper and middle back\n", " tilt_low = [] # slope between tailbase and middle back\n", " for rownum in range(len(df)):\n", " tilt_up.append((df.iloc[rownum, 7] - df.iloc[rownum, 10]) / (df.iloc[rownum, 6] - df.iloc[rownum, 9])) # slope between upper and middle back\n", " tilt_low.append((df.iloc[rownum, 13] - df.iloc[rownum, 10]) / (df.iloc[rownum, 12] - df.iloc[rownum, 9])) # slope between tailbase and middle back\n", " return(np.nanmin(tilt_up), np.nanmax(tilt_up), np.nanmean(tilt_up), np.nanstd(tilt_up), np.nanmin(tilt_low), np.nanmax(tilt_low), np.nanmean(tilt_low), np.nanstd(tilt_low))" ] }, { "cell_type": "markdown", "id": "989f73a8", "metadata": {}, "source": [ "#### Number of paws on beam:" ] }, { "cell_type": "code", "execution_count": 15, "id": "0333045a", "metadata": {}, "outputs": [], "source": [ "def paw_contact(df, stance_fp, swing_fp, stance_hp, swing_hp):\n", " double = 0\n", " single = 0\n", " zero = 0\n", " if len(stance_fp) > 0 and len(stance_hp) > 0:\n", " if stance_fp[0] > stance_hp[0]:\n", " begin = stance_fp[0]\n", " else:\n", " begin = stance_hp[0]\n", "\n", " if stance_fp[-1] < stance_hp[-1]:\n", " end = stance_fp[-1]\n", " else:\n", " end = stance_hp[-1]\n", " if len(stance_fp) > len(swing_fp):\n", " stance_fp = stance_fp[:-1]\n", " if len(stance_hp) > len(swing_hp):\n", " stance_hp = stance_hp[:-1]\n", " fp = [False for i in range(len(df))]\n", " hp = [False for i in range(len(df))]\n", " for i in range(len(stance_fp)):\n", " for j in range(stance_fp[i], swing_fp[i]):\n", " fp[j] = True\n", " for i in range(len(stance_hp)):\n", " for j in range(stance_hp[i], swing_hp[i]):\n", " hp[j] = True\n", " contact = []\n", " for i in range(len(df)):\n", " if fp[i] == False and hp[i] == False:\n", " contact.append(0)\n", " if fp[i] == True and hp[i] == False:\n", " contact.append(1)\n", " if fp[i] == False and hp[i] == True:\n", " contact.append(1)\n", " if fp[i] == True and hp[i] == True:\n", " contact.append(2)\n", "\n", " #if savePlots == True:\n", " #plt.plot(contact[begin:end])\n", " #plt.ylim(-0.05,2.05)\n", " #plt.xlabel(\"frames\")\n", " #plt.ylabel(\"no. of right paws on beam\")\n", " #plt.title(\"Paw contact: \" + short_file_name)\n", " #plt.savefig(out_dir+short_file_name+\"_paw_contact.png\")\n", " #plt.close()\n", "\n", " for i in contact[begin:end]:\n", " if i == 0:\n", " zero += 1\n", " if i == 1:\n", " single += 1\n", " if i == 2:\n", " double += 1\n", " if end-begin > 0:\n", " double = double/(end-begin)\n", " single = single/(end-begin)\n", " zero = zero/(end-begin)\n", " else:\n", " double = 1\n", " single = 0\n", " zero = 0\n", " return(zero, single, double)" ] }, { "cell_type": "markdown", "id": "07b15fa4", "metadata": {}, "source": [ "#### Find fraction of frames that mouse is hanging under beam" ] }, { "cell_type": "code", "execution_count": 16, "id": "7a420e82", "metadata": {}, "outputs": [], "source": [ "def under_beam(df):\n", " over = 0\n", " for rownum in range(len(df)):\n", " if df.iloc[rownum, 1] > 0:\n", " over += 1\n", " return((len(df)-over)/len(df))" ] }, { "cell_type": "code", "execution_count": 17, "id": "7dfcda8a", "metadata": {}, "outputs": [], "source": [ "colnames_data = ['snout.x', 'snout.y', 'snout.likelihood', 'neck.x', 'neck.y',\n", " 'neck.likelihood', 'upper_back.x', 'upper_back.y',\n", " 'upper_back.likelihood', 'middle_back.x', 'middle_back.y',\n", " 'middle_back.likelihood', 'tailbase.x', 'tailbase.y',\n", " 'tailbase.likelihood', 'shoulder_right.x', 'shoulder_right.y',\n", " 'shoulder_right.likelihood', 'elbow_right.x', 'elbow_right.y',\n", " 'elbow_right.likelihood', 'wrist_right.x', 'wrist_right.y',\n", " 'wrist_right.likelihood', 'forepaw_right.x', 'forepaw_right.y',\n", " 'forepaw_right.likelihood', 'forepaw_left.x', 'forepaw_left.y',\n", " 'forepaw_left.likelihood', 'hip_right.x', 'hip_right.y',\n", " 'hip_right.likelihood', 'knee_right.x', 'knee_right.y',\n", " 'knee_right.likelihood', 'ankle_right.x', 'ankle_right.y',\n", " 'ankle_right.likelihood', 'hindpaw_right.x', 'hindpaw_right.y',\n", " 'hindpaw_right.likelihood', 'hindpaw_left.x', 'hindpaw_left.y',\n", " 'hindpaw_left.likelihood', 'beam_left_top.x', 'beam_left_top.y',\n", " 'beam_left_top.likelihood', 'beam_left_bottom.x', 'beam_left_bottom.y',\n", " 'beam_left_bottom.likelihood', 'beam_right_top.x', 'beam_right_top.y',\n", " 'beam_right_top.likelihood', 'beam_right_bottom.x',\n", " 'beam_right_bottom.y', 'beam_right_bottom.likelihood']\n", "\n", "colnames_features = [\n", "\"file_name\", \"group\", \"reason_end\", \"time\", \"distance\", \"average_speed\",\n", "\n", "\"fp_cycle_dur_median\", \"fp_stance_dur_median\", \"fp_swing_dur_median\", \"fp_sw_st_ratio_median\", \"fp_stride_len_median\",\n", "\"hp_cycle_dur_median\", \"hp_stance_dur_median\", \"hp_swing_dur_median\", \"hp_sw_st_ratio_median\", \"hp_stride_len_median\",\n", "\"paw_dist_min\", \"paw_dist_max\", \"paw_dist_mean\", \"paw_dist_std\",\n", "\"fp_vel_min\", \"fp_vel_max\", \"fp_vel_mean\", \"fp_vel_std\", \"hp_vel_min\", \"hp_vel_max\", \"hp_vel_mean\", \"hp_vel_std\",\n", "\"tilt_up_min\", \"tilt_up_max\", \"tilt_up_mean\", \"tilt_up_std\", \"tilt_low_min\", \"tilt_low_max\", \"tilt_low_mean\", \"tilt_low_std\",\n", "\"snout_h_min\", \"snout_h_max\", \"snout_h_mean\", \"snout_h_std\",\n", "\"midb_h_min\", \"midb_h_max\", \"midb_h_mean\", \"midb_h_std\",\n", "\"no_paw_contact\", \"single_paw_contact\", \"double_paw_contact\",\n", "\n", "\"ank_h_min\", \"ank_h_max\", \"ank_h_mean\", \"ank_h_median\", \"ank_h_std\", \"ank_ang_min\", \"ank_ang_max\", \"ank_ang_mean\", \"ank_ang_median\", \"ank_ang_ROM\", \"ank_ang_std\", \"ank_angv_min\", \"ank_angv_max\", \"ank_angv_mean\", \"ank_angv_std\", \"ank_pos_min\", \"ank_pos_max\", \"ank_pos_mean\", \"ank_pos_std\", \"ank_anginit_min\", \"ank_anginit_max\", \"ank_anginit_mean\", \"ank_anginit_median\", \"ank_anginit_std\", \"ank_angpsw_min\", \"ank_angpsw_max\", \"ank_angpsw_mean\", \"ank_angpsw_median\", \"ank_angpsw_std\",\n", "\"kn_h_min\", \"kn_h_max\", \"kn_h_mean\", \"kn_h_median\", \"kn_h_std\", \"kn_ang_min\", \"kn_ang_max\", \"kn_ang_mean\", \"kn_ang_median\", \"kn_ang_ROM\", \"kn_ang_std\", \"kn_angv_min\", \"kn_angv_max\", \"kn_angv_mean\", \"kn_angv_std\", \"kn_pos_min\", \"kn_pos_max\", \"kn_pos_mean\", \"kn_pos_std\", \"kn_anginit_min\", \"kn_anginit_max\", \"kn_anginit_mean\", \"kn_anginit_median\", \"kn_anginit_std\", \"kn_angpsw_min\", \"kn_angpsw_max\", \"kn_angpsw_mean\", \"kn_angpsw_median\", \"kn_angpsw_std\",\n", "\"hip_h_min\", \"hip_h_max\", \"hip_h_mean\", \"hip_h_median\", \"hip_h_std\", \"hip_ang_min\", \"hip_ang_max\", \"hip_ang_mean\", \"hip_ang_median\", \"hip_ang_ROM\", \"hip_ang_std\", \"hip_angv_min\", \"hip_angv_max\", \"hip_angv_mean\", \"hip_angv_std\", \"hip_pos_min\", \"hip_pos_max\", \"hip_pos_mean\", \"hip_pos_std\", \"hip_anginit_min\", \"hip_anginit_max\", \"hip_anginit_mean\", \"hip_anginit_median\", \"hip_anginit_std\", \"hip_angpsw_min\", \"hip_angpsw_max\", \"hip_angpsw_mean\", \"hip_angpsw_median\", \"hip_angpsw_std\",\n", "\"wr_h_min\", \"wr_h_max\", \"wr_h_mean\", \"wr_h_median\", \"wr_h_std\", \"wr_ang_min\", \"wr_ang_max\", \"wr_ang_mean\", \"wr_ang_median\", \"wr_ang_ROM\", \"wr_ang_std\", \"wr_angv_min\", \"wr_angv_max\", \"wr_angv_mean\", \"wr_angv_std\", \"wr_pos_min\", \"wr_pos_max\", \"wr_pos_mean\", \"wr_pos_std\", \"wr_anginit_min\", \"wr_anginit_max\", \"wr_anginit_mean\", \"wr_anginit_median\", \"wr_anginit_std\", \"wr_angpsw_min\", \"wr_angpsw_max\", \"wr_angpsw_mean\", \"wr_angpsw_median\", \"wr_angpsw_std\",\n", "\"el_h_min\", \"el_h_max\", \"el_h_mean\", \"el_h_median\", \"el_h_std\", \"el_ang_min\", \"el_ang_max\", \"el_ang_mean\", \"el_ang_median\", \"el_ang_ROM\", \"el_ang_std\", \"el_angv_min\", \"el_angv_max\", \"el_angv_mean\", \"el_angv_std\", \"el_pos_min\", \"el_pos_max\", \"el_pos_mean\", \"el_pos_std\", \"el_anginit_min\", \"el_anginit_max\", \"el_anginit_mean\", \"el_anginit_median\", \"el_anginit_std\", \"el_angpsw_min\", \"el_angpsw_max\", \"el_angpsw_mean\", \"el_angpsw_median\", \"el_angpsw_std\",\n", "\"sh_h_min\", \"sh_h_max\", \"sh_h_mean\", \"sh_h_median\", \"sh_h_std\", \"sh_ang_min\", \"sh_ang_max\", \"sh_ang_mean\", \"sh_ang_median\", \"sh_ang_ROM\", \"sh_ang_std\", \"sh_angv_min\", \"sh_angv_max\", \"sh_angv_mean\", \"sh_angv_std\", \"sh_pos_min\", \"sh_pos_max\", \"sh_pos_mean\", \"sh_pos_std\", \"sh_anginit_min\", \"sh_anginit_max\", \"sh_anginit_mean\", \"sh_anginit_median\", \"sh_anginit_std\", \"sh_angpsw_min\", \"sh_angpsw_max\", \"sh_angpsw_mean\", \"sh_angpsw_median\", \"sh_angpsw_std\",\n", "\n", "\"hdrop_num_abs\", \"hdrop_num_rel\",\n", "\"under_beam\"\n", "\n", "]\n", "\n" ] }, { "cell_type": "markdown", "id": "733091c1", "metadata": {}, "source": [ "### This is where the functions are called:" ] }, { "cell_type": "code", "execution_count": null, "id": "08edd6be", "metadata": { "scrolled": true, "tags": [] }, "outputs": [], "source": [ "file_names = glob.glob(file_directory+\"/*.csv\")\n", "if not os.path.exists(out_dir):\n", " os.mkdir(out_dir)\n", "res_data = []\n", "col_match = []\n", "hdrop_data = []\n", "fps_dict = {}\n", "with open(videos_fps_file, \"r\") as fps_file:\n", " for num, line in enumerate(fps_file):\n", " if num > 1:\n", " line = line.split(\",\")\n", " key = line[1].replace(\"_cropped\", \"\").strip(\"\\\"\")\n", " value = line[2].strip(\"\\\"\")\n", " fps_dict[key] = float(value)\n", "print(\"Number of files to process: {}\".format(len(file_names)))\n", "for num, file_name in enumerate(file_names):\n", " file = file_name.split(\"\\\\\")[-1]\n", " group = file_name.split(\"\\\\\")[-2]\n", " if not os.path.exists(out_dir+group+\"/\"):\n", " os.mkdir(out_dir+group+\"/\")\n", " print(\"***************************************************\")\n", " print(\"processing: file {} of {}, file name: {}\".format(num + 1, len(file_names), file))\n", " short_file_name = file.split(\"_cropped\")[0]\n", " # load file as data frame\n", " print(\"step 1/11: data loading\")\n", " df = pd.read_csv(file_name) # step 1: data loading\n", " fps = fps_dict[short_file_name]\n", " print(\"step 2/11: data prep\")\n", " df, end_reason, time, distance, speed = prep(df, short_file_name, fps)\n", " if saveFiles == True:\n", " df.to_csv(out_dir+group+\"/\"+short_file_name+\"_preprocessed_nan_crop.csv\", index=False) # pre-processed file\n", " col_match.append(list(df) == colnames_data)\n", "\n", " # add other features here with out.extend(list) or out.append(single value)\n", " out = [file, group, end_reason, time, distance, speed]\n", "\n", " print(\"step 3.1/11: finding step cycles forepaw\")\n", " if len(df) > 3:\n", " fp_cycle_dur_mean, fp_stance_dur_mean, fp_swing_dur_mean, fp_sw_st_ratio, fp_stride_len_mean, fp_st_begin, fp_sw_begin = find_cycles_forepaw(df)\n", " out.extend([fp_cycle_dur_mean, fp_stance_dur_mean, fp_swing_dur_mean, fp_sw_st_ratio, fp_stride_len_mean])\n", " else:\n", " out.extend([np.NAN, np.NAN, np.NAN, np.NAN, np.NAN])\n", " print(\"step 3.2/11: finding step cycles hindpaw\")\n", " if len(df) > 3:\n", " hp_cycle_dur_mean, hp_stance_dur_mean, hp_swing_dur_mean, hp_sw_st_ratio, hp_stride_len_mean, hp_st_begin, hp_sw_begin, minima_hp = find_cycles_hindpaw(df)\n", " out.extend([hp_cycle_dur_mean, hp_stance_dur_mean, hp_swing_dur_mean, hp_sw_st_ratio, hp_stride_len_mean])\n", " else:\n", " out.extend([np.NAN, np.NAN, np.NAN, np.NAN, np.NAN])\n", " print(\"step 4/11: paw distance\")\n", " out.extend(paw_distance(df))\n", " print(\"step 5/11: paw velocity\")\n", " out.extend(paw_velocity(df)) \n", " print(\"step 6/11: trunk tilt\")\n", " out.extend(trunk_tilt(df))\n", " print(\"step 7/11: snout height\")\n", " out.extend([np.nanmin(df.iloc[:, 1]), np.nanmax(df.iloc[:, 1]), np.nanmean(df.iloc[:, 1]), np.nanstd(df.iloc[:, 1])])\n", " print(\"step 8/11: midback height\")\n", " out.extend([np.nanmin(df.iloc[:, 10]), np.nanmax(df.iloc[:, 10]), np.nanmean(df.iloc[:, 10]), np.nanstd(df.iloc[:, 10])])\n", " print(\"step 9/11: paw contact\")\n", " out.extend(paw_contact(df, fp_st_begin, fp_sw_begin, hp_st_begin, hp_sw_begin))\n", " print(\"step 10/11: angle features\")\n", " out.extend(feature_calc(df, fp_st_begin, fp_sw_begin, hp_st_begin, hp_sw_begin, short_file_name, fps))\n", " print(\"step 11/11: hindlimb drops\")\n", " hdrop_abs, hdrop_rel, paw_h, ank_ang, knee_ang, hip_ang, tilt_up, tilt_low, midb_h, tb_h = hindlimb_drops(df, distance, minima_hp)\n", " out.extend([hdrop_abs, hdrop_rel])\n", " hdrop_data.append([file, hdrop_abs, hdrop_rel, paw_h, ank_ang, knee_ang, hip_ang, tilt_up, tilt_low, midb_h, tb_h])\n", " out.append(under_beam(df))\n", " ##\n", " res_data.append(out)\n", "\n", " print(\"Analysis of file finished.\")\n", "\n", "if all(col_match) == True:\n", " print(\"***************************************************\")\n", " print(\"Analysis has finished, all column names match.\")\n", "else:\n", " print(\"!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\")\n", " print(\"Analysis has finished, there is a PROBLEM with the column names\".\n", "\n", "res_table = pd.DataFrame(res_data, columns=colnames_features)\n", "pd.set_option('display.max_columns', None)\n", "res_table\n", "#res_table.to_csv(\"C:/Users/franz/Documents/Bachelor Studium/BachelorArbeit/JupyterLabBA/DLCfiles_results/res_file.csv\", index=False) # result file containing calculated features\n", "res_table.to_csv(out_dir+\"res_file.csv\", index=False)\n", "print(\"Results are saved in file: \\\"{}\\\"\".format(out_dir+\"res_file.csv\"))\n", "\n", "if saveFiles == True:\n", " hdrop_df = pd.DataFrame(hdrop_data, columns=[\"file\", \"hdrop_abs_num\", \"hdrop_rel_num\", \"hdrop_pawh_mean\", \"hdrop_ank_ang_mean\", \"hdrop_kn_ang_mean\", \"hdrop_hip_ang_mean\", \"hdrop_tilt_up_mean\", \"hdrop_tilt_low_mean\", \"hdrop_midb_h_mean\", \"hdrop_tb_h_mean\"] )\n", " hdrop_df.to_csv(out_dir+\"hindlimbdrops.csv\", index=False) # file containing hindlimb drop features" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" } }, "nbformat": 4, "nbformat_minor": 5 }