Closed Nike-Nay closed 10 months ago
For my multi animal videos and .csv files I needed to have an individual .csv file for each animal. VAME requires you to have an individual video for each animal (root) also. Because I didn't want to have the same video with a different name for each animal, I just created four (one for each animal in my video files) video aliases for each video. Each alias file just points to the actual video file so you don't have to have one for each animal. I wrote this function to make those alias files:
def create_symlinks(video_dir, new_dir):
"""_summary_
Args:
video_dir (str): The path to the directory containing the video files.
new_dir (str): The path to the directory where the video files will be moved and the symbolic links will be created.
1. It moves the video files from the original directory (video_dir) to a new directory (new_dir).
2. It creates symbolic links in the original directory that point to the moved video files in the new directory.
"""
# Create the new directory if it doesn't exist
os.makedirs(new_dir, exist_ok=True)
# Get a list of all video files in the directory
video_files = glob.glob(os.path.join(video_dir, '*.mp4')) # adjust the extension if needed
# List of rat names
rat_names = ['Rat1', 'Rat2', 'Rat3', 'Rat4']
# Loop through each video file
for video_path in video_files:
# Get the video filename
video_filename = os.path.basename(video_path)
# Move the video file to the new directory
new_video_path = os.path.join(new_dir, video_filename)
shutil.move(video_path, new_video_path)
# Create a symbolic link for each rat name
for rat_name in rat_names:
# Path for the symbolic link
symlink_path = os.path.join(video_dir, os.path.splitext(video_filename)[0] + '_' + rat_name + os.path.splitext(video_filename)[1])
# Create the symbolic link
os.symlink(new_video_path, symlink_path)
# Usage:
# create_symlinks('/path/to/videos', '/path/to/new_directory')
NOTE: Be sure to change the 'rat_names' to your root names (or however you are naming each root).
After this, you will need to update the video files listed in your configuration file to reflect the symlinks. You can do that with:
def update_video_sets_in_config(config_path, video_dir):
# Get a list of all video files in the directory
video_files = glob.glob(os.path.join(video_dir, '*.mp4')) # adjust the extension if needed
# Get the video names (without extension)
video_names = [os.path.splitext(os.path.basename(video_file))[0] for video_file in video_files]
# Create a YAML object
yaml = YAML()
# Load the existing config data
with open(config_path, 'r') as file:
config_data = yaml.load(file)
# Update the 'video_sets' field
config_data['video_sets'] = video_names
# Write the updated config data back to the file
with open(config_path, 'w') as file:
yaml.dump(config_data, file)
# Usage:
# update_video_sets_in_config('/path/to/config.yaml', '/path/to/videos')
Be sure to back up your video files and configuration file before using these functions.
To use them:
Let me know if you have any questions @Nike-Nay !
Also, in vame/util/align_egocentrical.py you may need to exchange the original 'def alignment' with this one:
def alignment(config, filename, pose_ref_index, video_format, crop_size, use_video=False, check_video=False, save=False, blank_background=True):
"""
Parameters
----------
config : string
Path to config file
filename : string
Name of video to be processed (without extension)
pose_ref_index : list
List of 2 body parts to egocentrically align to.
video_format : string
Extension of filename.
crop_size : tuple
Size to crop aligned data to. Should be larger than maximum single-frmae movement.
use_video : Tbool, optional (default False)
Whether to use the video or only DLC poses.
check_video : bool, optional (Default False)
Whether to display the video. Press Q to exit while displaying.
save : bool, optional, (default False)
Whether to save the video instead of displaying.
Returns
-------
time_series : list
Frame indices
frames : list of arrays
Aligned poses or video frames.
"""
config_file = Path(config).resolve()
cfg = read_config(config_file)
path_to_file = cfg['project_path']
confidence = cfg['pose_confidence']
#read out data
dataFile = glob.glob(os.path.join(path_to_file,'videos','pose_estimation',filename+'*.csv'))
if len(dataFile)>1:
raise KeyError("Multiple csv files match video filename")
else:
dataFile=dataFile[0]
dlc_data_type = input("Were your DLC .csv files orginially multi-animal? (y/n): ")
if dlc_data_type == 'n':
data = pd.read_csv(dataFile, skiprows=0, index_col=0, header=[1,2])
if dlc_data_type == 'y':
data = pd.read_csv(dataFile, index_col=0, header=[0, 1])
#data = pd.read_csv(os.path.join(path_to_file,'videos','pose_estimation',filename+'.csv'), skiprows = 0, header=[1,2], index_col=0)
data_mat = pd.DataFrame.to_numpy(data)
# get the coordinates for alignment from data table
pose_list = []
for i in range(int(data_mat.shape[1]/3)):
pose_list.append(data_mat[:,i*3:(i+1)*3])
#list of reference coordinate indices for alignment
#0: snout, 1: forehand_left, 2: forehand_right,
#3: hindleft, 4: hindright, 5: tail
pose_ref_index = pose_ref_index
#list of 2 reference coordinate indices for avoiding flipping
pose_flip_ref = pose_ref_index
if use_video:
if not blank_background:
#compute background
bg = background(path_to_file,filename,video_format)
elif blank_background:
bg=0
capture = cv.VideoCapture(os.path.join(path_to_file,'videos',filename+video_format))
if not capture.isOpened():
raise Exception("Unable to open video file: {0}".format(os.path.join(path_to_file,'videos',filename+video_format)))
frame_count = int(capture.get(cv.CAP_PROP_FRAME_COUNT))
capture.release()
else:
bg = 0
frame_count = len(data) # Change this to an abitrary number if you first want to test the code
frames, n, time_series = align_mouse(path_to_file, filename, video_format, crop_size, pose_list, pose_ref_index,
confidence, pose_flip_ref, bg, frame_count, use_video=use_video)
if check_video and not save:
play_aligned_video(frames, n, frame_count, path_to_file)
elif check_video and save:
play_aligned_video(frames, n, frame_count, path_to_file, save=True, filename=filename, crop_size=crop_size)
return time_series, frames
The original VAME does not adjust how it processes the headers of the .csv files based on whether or not they were originally multi animal .csv files. The conversion of the .csv files from multi animal to individual animal removes the header row of the .csv file and the original code does account for this.
Thank you so much for getting back to me! Your advice was super helpful. I will close this issue now since this solved my problem!
I am using videos of roots growing. There are 5 roots in each video. I did not use DeepLabCut but matched the coordinate data I got from a different plugin to that which you would get from DLC.
Therefore, I have 5 csv files for each video. One for each root. Do I need to combine these in one in order to use them in VAME?
I am aware a similar issue was opened May 30, 2021 ("How to deal with multi-animals?#54"). However, I did not understand if I need to keep them separated or in one big csv file?
Thank you for any responses!