fetch draft sync from other branch
This commit is contained in:
parent
1246c2813b
commit
f0a6049fb0
236
Pose2Sim/Utilities/synchronize_cams_draft.py
Normal file
236
Pose2Sim/Utilities/synchronize_cams_draft.py
Normal file
@ -0,0 +1,236 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy import signal
|
||||
from scipy import interpolate
|
||||
import json
|
||||
import os
|
||||
import fnmatch
|
||||
import pickle as pk
|
||||
|
||||
|
||||
'''
|
||||
#########################################
|
||||
## Synchronize cameras ##
|
||||
#########################################
|
||||
|
||||
Steps undergone in this script
|
||||
0. Converting json files to pandas dataframe
|
||||
1. Computing speeds (either vertical, or 2D speeds)
|
||||
2. Plotting paired correlations of speeds from one camera viewpoint to another (work on one single keypoint, or on all keypoints, or on a weighted selection of keypoints)
|
||||
3.
|
||||
Dans l'idéal, on fait ça automatiqueement pour toutes les vues, en coisissant les paires 2 à 2 avec le plus haut coefficient de corrélation,
|
||||
et on demande confirmation avant de supprimer les frames en question (en réalité, renommées .json.del - option reset_sync dans le Config.toml)
|
||||
'''
|
||||
|
||||
|
||||
#############
|
||||
# CONSTANTS #
|
||||
#############
|
||||
|
||||
# pose_dir is populated with subfolders for each camera, each of them populated with json files
|
||||
pose_dir = r'GOp2AniPoitiersHalteroHaltero2pose-2d'
|
||||
fps = 120 # frame rate of the cameras (Hz)
|
||||
reset_sync = True # Start synchronization over each time it is run
|
||||
|
||||
cut_off_frequency = 10 # cut-off frequency for a 4th order low-pass Butterworth filter
|
||||
|
||||
# Vertical speeds (on X, Y, or Z axis, or 2D speeds)
|
||||
speed_kind = 'y' # 'x', 'y', 'z', or '2D'
|
||||
vmax = 20 # pxs
|
||||
|
||||
cam1_nb = 4
|
||||
cam2_nb = 3
|
||||
id_kpt = [9,10] # Pour plus tard aller chercher numéro depuis keypoint name dans skeleton.py. 'RWrist' BLAZEPOSE 16, BODY_25B 10, BODY_25 4 ; 'LWrist' BLAZEPOSE 15, BODY_25B 9, BODY_25 7
|
||||
weights_kpt = [1,1] # Pris en compte uniquement si on a plusieurs keypoints
|
||||
frames = [2850,3490]
|
||||
|
||||
|
||||
############
|
||||
# FUNCTIONS#
|
||||
############
|
||||
|
||||
def convert_json2csv(json_dir)
|
||||
json_files_names = fnmatch.filter(os.listdir(os.path.join(json_dir)), '.json')
|
||||
json_files_path = [os.path.join(json_dir, j_f) for j_f in json_files_names]
|
||||
json_coords = []
|
||||
for i, j_p in enumerate(json_files_path)
|
||||
# if i in range(frames)
|
||||
with open(j_p) as j_f
|
||||
try
|
||||
json_data = json.load(j_f)['people'][0]['pose_keypoints_2d']
|
||||
except
|
||||
print(f'No person found in {os.path.basename(json_dir)}, frame {i}')
|
||||
json_data = [0]75
|
||||
json_coords.append(json_data)
|
||||
df_json_coords = pd.DataFrame(json_coords)
|
||||
return df_json_coords
|
||||
|
||||
def drop_col(df,col_nb)
|
||||
idx_col = list(range(col_nb-1, df.shape[1], col_nb))
|
||||
df_dropped = df.drop(idx_col, axis=1)
|
||||
df_dropped.columns = range(df_dropped.columns.size)
|
||||
return df_dropped
|
||||
|
||||
def speed_vert(df, axis='y')
|
||||
axis_dict = {'x'0, 'y'1, 'z'2}
|
||||
df_diff = df.diff()
|
||||
df_diff = df_diff.fillna(df_diff.iloc[1]2)
|
||||
df_vert_speed = pd.DataFrame([df_diff.loc[, 2k + axis_dict[axis]] for k in range(int(df_diff.shape[1]2))]).T
|
||||
df_vert_speed.columns = np.arange(len(df_vert_speed.columns))
|
||||
return df_vert_speed
|
||||
|
||||
def speed_2D(df)
|
||||
df_diff = df.diff()
|
||||
df_diff = df_diff.fillna(df_diff.iloc[1]2)
|
||||
df_2Dspeed = pd.DataFrame([np.sqrt(df_diff.loc[,2k]2 + df_diff.loc[,2k+1]2) for k in range(int(df_diff.shape[1]2))]).T
|
||||
return df_2Dspeed
|
||||
|
||||
def interpolate_nans(col, kind)
|
||||
'''
|
||||
Interpolate missing points (of value nan)
|
||||
|
||||
INPUTS
|
||||
- col pandas column of coordinates
|
||||
- kind 'linear', 'slinear', 'quadratic', 'cubic'. Default 'cubic'
|
||||
|
||||
OUTPUT
|
||||
- col_interp interpolated pandas column
|
||||
'''
|
||||
|
||||
idx = col.index
|
||||
idx_good = np.where(np.isfinite(col))[0] #index of non zeros
|
||||
if len(idx_good) = 10 return col
|
||||
# idx_notgood = np.delete(np.arange(len(col)), idx_good)
|
||||
|
||||
if not kind # 'linear', 'slinear', 'quadratic', 'cubic'
|
||||
f_interp = interpolate.interp1d(idx_good, col[idx_good], kind=cubic, bounds_error=False)
|
||||
else
|
||||
f_interp = interpolate.interp1d(idx_good, col[idx_good], kind=kind[0], bounds_error=False)
|
||||
col_interp = np.where(np.isfinite(col), col, f_interp(idx)) #replace nans with interpolated values
|
||||
col_interp = np.where(np.isfinite(col_interp), col_interp, np.nanmean(col_interp)) #replace remaining nans
|
||||
|
||||
return col_interp #, idx_notgood
|
||||
|
||||
def plot_time_lagged_cross_corr(camx, camy, ax)
|
||||
pearson_r = [camx.corr(camy.shift(lag)) for lag in range(-2fps, 2fps)] # lag -2 sec à +2 sec
|
||||
offset = int(np.floor(len(pearson_r)2)-np.argmax(pearson_r))
|
||||
max_corr = np.max(pearson_r)
|
||||
ax.plot(list(range(-2fps, 2fps)), pearson_r)
|
||||
ax.axvline(np.ceil(len(pearson_r)2)-2fps,color='k',linestyle='--')
|
||||
ax.axvline(np.argmax(pearson_r)-2fps,color='r',linestyle='--',label='Peak synchrony')
|
||||
plt.annotate(f'Max correlation={np.round(max_corr,2)}', xy=(0.05, 0.9), xycoords='axes fraction')
|
||||
ax.set(title=f'Offset = {offset} frames', xlabel='Offset (frames)',ylabel='Pearson r')
|
||||
plt.legend()
|
||||
return offset, max_corr
|
||||
|
||||
|
||||
######################################
|
||||
# 0. CONVERTING JSON FILES TO PANDAS #
|
||||
######################################
|
||||
|
||||
# Also filter, and then save
|
||||
|
||||
pose_listdirs_names = next(os.walk(pose_dir))[1]
|
||||
json_dirs_names = [k for k in pose_listdirs_names if 'json' in k]
|
||||
json_dirs = [os.path.join(pose_dir, j_d) for j_d in json_dirs_names]
|
||||
|
||||
df_coords = []
|
||||
for i, json_dir in enumerate(json_dirs)
|
||||
df_coords.append(convert_json2csv(json_dir))
|
||||
df_coords[i] = drop_col(df_coords[i],3) # drop likelihood
|
||||
|
||||
b, a = signal.butter(42, cut_off_frequency(fps2), 'low', analog = False)
|
||||
for i in range(len(json_dirs))
|
||||
df_coords[i] = pd.DataFrame(signal.filtfilt(b, a, df_coords[i], axis=0)) # filter
|
||||
|
||||
## Pour sauvegarder et réouvrir au besoin
|
||||
with open(os.path.join(pose_dir, 'coords'), 'wb') as fp
|
||||
pk.dump(df_coords, fp)
|
||||
# with open(os.path.join(pose_dir, 'coords'), 'rb') as fp
|
||||
# df_coords = pk.load(fp)
|
||||
|
||||
|
||||
#############################
|
||||
# 1. COMPUTING SPEEDS #
|
||||
#############################
|
||||
|
||||
|
||||
|
||||
# Vitesse verticale
|
||||
df_speed = []
|
||||
for i in range(len(json_dirs))
|
||||
if speed_kind == 'y'
|
||||
df_speed.append(speed_vert(df_coords[i]))
|
||||
elif speed_kind == '2D'
|
||||
df_speed.append(speed_2D(df_coords[i]))
|
||||
df_speed[i] = df_speed[i].where(df_speed[i]vmax, other=np.nan)
|
||||
df_speed[i] = df_speed[i].apply(interpolate_nans, axis=0, args = ['cubic'])
|
||||
|
||||
|
||||
#############################################
|
||||
# 2. PLOTTING PAIRED CORRELATIONS OF SPEEDS #
|
||||
#############################################
|
||||
|
||||
# Faire ça sur toutes les paires de cams
|
||||
# Choisir paire avec corrélation la plus haute
|
||||
|
||||
|
||||
# sur un point particulier (typiquement le poignet sur un mouvement vertical)
|
||||
# ou sur tous les points
|
||||
# ou sur une sélection de points pondérés
|
||||
|
||||
id_kpt_dict = {}
|
||||
|
||||
if len(id_kpt)==1 and id_kpt != ['all']
|
||||
camx = df_speed[cam1_nb-1].loc[range(np.array(frames)),id_kpt[0]]
|
||||
camy = df_speed[cam2_nb-1].loc[range(np.array(frames)),id_kpt[0]]
|
||||
elif id_kpt == ['all']
|
||||
camx = df_speed[cam1_nb-1].loc[range(np.array(frames)),].sum(axis=1)
|
||||
camy = df_speed[cam2_nb-1].loc[range(np.array(frames)),].sum(axis=1)
|
||||
elif len(id_kpt)1 and len(id_kpt)==len(weights_kpt) # ex id_kpt1=9 set to 10, id_kpt2=10 to 15
|
||||
# ajouter frames
|
||||
dict_id_weights = {iw for i, w in zip(id_kpt, weights_kpt)}
|
||||
camx = df_speed[cam1_nb-1].dot(pd.Series(dict_id_weights).reindex(df_speed[cam1_nb-1].columns, fill_value=0))
|
||||
camy = df_speed[cam2_nb-1].dot(pd.Series(dict_id_weights).reindex(df_speed[cam2_nb-1].columns, fill_value=0))
|
||||
camx = camx.loc[range(np.array(frames))]
|
||||
camy = camy.loc[range(np.array(frames))]
|
||||
else
|
||||
raise ValueError('wrong values for id_kpt or weights_kpt')
|
||||
|
||||
|
||||
f, ax = plt.subplots(2,1)
|
||||
# speed
|
||||
camx.plot(ax=ax[0], label = f'cam {cam1_nb}')
|
||||
camy.plot(ax=ax[0], label = f'cam {cam2_nb}')
|
||||
ax[0].set(xlabel='Frame',ylabel='Speed (pxframe)')
|
||||
ax[0].legend()
|
||||
|
||||
# time lagged cross-correlation
|
||||
offset, max_corr = plot_time_lagged_cross_corr(camx, camy, ax[1])
|
||||
|
||||
f.tight_layout()
|
||||
plt.show()
|
||||
|
||||
|
||||
##################################################################
|
||||
# 3. ON CHANGE LES EXTENSIONS DES FICHIERS POUR SIMULER UN OFFSET#
|
||||
##################################################################
|
||||
|
||||
# et on relance tout le code
|
||||
|
||||
|
||||
if offset 0
|
||||
json_dir_to_offset = json_dirs[cam2_nb-1]
|
||||
else
|
||||
json_dir_to_offset = json_dirs[cam1_nb-1]
|
||||
offset = -offset
|
||||
|
||||
json_files = fnmatch.filter(os.listdir(json_dir_to_offset), '.json')[offset]
|
||||
|
||||
[os.rename( os.path.join(json_dir_to_offset,json_file), os.path.join(json_dir_to_offset,json_file+'.old') ) for json_file in json_files]
|
||||
|
||||
# Reset remove all '.old'
|
||||
json_files = fnmatch.filter(os.listdir(json_dir_to_offset), '.old')
|
||||
[os.rename( os.path.join(json_dir_to_offset,json_file), os.path.join(json_dir_to_offset,json_file[-4]) ) for json_file in json_files]
|
||||
|
@ -375,7 +375,7 @@ If you already have a calibration file, set `calibration_type` type to `convert`
|
||||
> _**Cameras need to be synchronized, so that 2D points correspond to the same position across cameras.**_\
|
||||
*N.B.: Skip this step if your cameras are already synchronized.*
|
||||
|
||||
If your cameras are not natively synchronized, you can use [this script](https://github.com/perfanalytics/pose2sim/blob/draft/Pose2Sim/Utilities/synchronize_cams.py).\
|
||||
If your cameras are not natively synchronized, you can use [this script](https://github.com/perfanalytics/pose2sim/blob/main/Pose2Sim/Utilities/synchronize_cams_draft.py). This is still a draft, and will be updated in the future.\
|
||||
Alternatively, use a clap, a flash, or a beep noise to synchronize them.
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user