How to use the PigeonSuperModel DataSet
Contents
How to use the PigeonSuperModel DataSet¶
Download this notebook in the upper right corner as .ipynb
file and start it from within your DeepLabCut environment. If you need to install DeepLabCut first, please check here.
This notebook will show you how to create a pretrained project with the PigeonSuperModel and how to download and merge the pre-labeled PigeonSuperModel dataset to increase your own training data:
Create a new PigeonSuperModel Project
Extract new Frames
Label your own Data
Merge PigeonSuperModel DataSet
Re-Train the PigeonSuperModel
Evaluate your new PigeonSuperModel
Contribute to PigeonSuperModel.com
import os
import deeplabcut
from tkinter import filedialog
print(f'Using DeepLabCut version: {deeplabcut.__version__}')
Create a new project¶
See Notebook before
video_path = r'F:\PigeonSuperModel_local\ForAnalysisVideoData\StopSignal'
video_list = os.listdir(video_path)
projectpath = r'F:\PigeonSuperModel_local\DeepLabCutModels'
def monkeypatch():
'''
This function locates a file named 'pose_estimation_tensorflow/models/pretrained/pretrained_model_urls.yaml' in your local DeepLabCut
installation and adds the new URLs below to reach the PigeonSUperModel repository.
In a last step, the names of the PigeonSuperModels are added to the 'modelzoo.py' Modeloptions as valid model names.
'''
from deeplabcut.create_project import modelzoo
# locate the `pretrained_model_urls.yaml` file in your local DeepLabCut installation (hardcoded based on deeplabcut dir structure)
neturls_path = os.path.join(deeplabcut.auxiliaryfunctions.get_deeplabcut_path(), 'pose_estimation_tensorflow', 'models', 'pretrained', 'pretrained_model_urls.yaml')
edits = {'PigeonSuperModel_resnet_50': 'https://gitlab.ruhr-uni-bochum.de/ikn/pigeonsupermodel/-/raw/main/models/DeepLabCut/DLC_PigeonSuperModel_01_resnet_50_iteration-2_shuffle-3.tar.gz',
'PigeonSuperModel_resnet_101': 'https://gitlab.ruhr-uni-bochum.de/ikn/pigeonsupermodel/-/raw/main/models/DeepLabCut/DLC_PigeonSuperModel_01_resnet_101_iteration-2_shuffle-4.tar.gz',
'PigeonSuperModel_resnet_152': 'https://gitlab.ruhr-uni-bochum.de/ikn/pigeonsupermodel/-/raw/main/models/DeepLabCut/DLC_PigeonSuperModel_01_resnet_152_iteration-2_shuffle-5.tar.gz'}
# add new model to the neturls file
deeplabcut.auxiliaryfunctions.edit_config(neturls_path, edits)
# add new model to locally loaded Modeloptions from modelzoo.py
modelzoo.Modeloptions.extend(['PigeonSuperModel_resnet_50', 'PigeonSuperModel_resnet_101', 'PigeonSuperModel_resnet_152'])
# you may need admin rights on your computer to edit the files above
monkeypatch()
def create_dummy_videos(target_dir):
import urllib.request
import cv2
# create dummy video
logolink = 'https://gitlab.ruhr-uni-bochum.de/ikn/pigeonsupermodel/-/raw/main/PigeonSuperModel.png?inline=false'
logo = os.path.join(target_dir, 'logo.png')
urllib.request.urlretrieve(logolink, logo);
dummyvideo = os.path.join(target_dir, 'logo.mp4')
img_array = []
for i in range(20):
img = cv2.imread(logo)
height, width, layers = img.shape
img_array.append(img)
os.remove(logo)
out = cv2.VideoWriter(dummyvideo, cv2.VideoWriter_fourcc(*'mp4v'), 20, (width, height))
for i in range(len(img_array)):
out.write(img_array[i])
out.release()
return dummyvideo
# create dummy video to use copy=True
video_path = create_dummy_videos(projectpath)
# create pre-trained model from PigeonSuperModel
config_path, _ = deeplabcut.create_pretrained_project(
'StopSignal',
'PigeonSuperModel',
[video_path],
videotype='mp4',
copy_videos=True,
working_directory=projectpath,
model='PigeonSuperModel_resnet_50',
analyzevideo=False,
filtered=False,
createlabeledvideo=False,
)
os.remove(video_path)
sign = {'PigeonSuperModel': f'The pretrained PigeonSuperModel was added to this project. (C) PigeonSuperModel.com'}
deeplabcut.auxiliaryfunctions.edit_config(config_path, sign)
Copying the videos may take very long, and creating hyperlinks does not always work on windows. Alternatively,
we can add the video paths to the config.yaml
file while leaving our video data on a separate disk.
# patch: add video links to config.yaml
def add_videopaths(config, video_list):
from pathlib import Path
from deeplabcut.utils.auxfun_videos import VideoReader
videos = video_list
original_video_sets = deeplabcut.auxiliaryfunctions.read_config(config)['video_sets']
projectpath = deeplabcut.auxiliaryfunctions.read_config(config)['project_path']
# create new dict for video_sets
video_sets = {}
for video in videos:
print(f'Adding video: {video}')
try:
rel_video_path = str(Path.resolve(Path(video)))
vid = VideoReader(rel_video_path)
video_sets[rel_video_path] = {"crop": ", ".join(map(str, vid.get_bbox()))}
except:
print(f'Could not read video, writing fake frame dimensions ...')
rel_video_path = str(Path.resolve(Path(video)))
video_sets[rel_video_path] = {"crop": ", ".join(map(str, [0, 1920, 0, 1080]))} # how bad is this
# create labels directory
labeldir = os.path.basename(video).split('.')[0]
if os.path.isdir(os.path.join(projectpath,'labeled-data', labeldir)):
print('Labels directory already exists!')
else:
print(f'Creating directoy: {labeldir}')
os.mkdir(os.path.join(projectpath,'labeled-data', labeldir))
# expand existing video_sets
if original_video_sets is None:
new_video_sets = video_sets
else:
new_video_sets = {**original_video_sets, **video_sets}
# add videos to project
edit = {'video_sets': new_video_sets}
deeplabcut.auxiliaryfunctions.edit_config(config, edit);
return
# add video paths
#video_list = filedialog.askopenfilenames(title = 'Choose video paths to include to your project')
add_videopaths(config_path, video_list)
Alternatively you can just load a previously created model:
# load new project (see Notebook before to create a new project)
config = filedialog.askopenfilenames(title='Choose the config.yaml file of your DeepLabCut project:')
config_path = config[0]
print(f'Using project path: {config_path}')
Optional: Create 3D project to match synchronous frames¶
# create 3d config
config_3d = deeplabcut.create_new_project_3d('StopSignal','3D',num_cameras = 2, working_directory=deeplabcut.auxiliaryfunctions.read_config(config_path)['project_path'])
# change settings
edits = {'camera_names': ['camR', 'camL']}
deeplabcut.auxiliaryfunctions.edit_config(config_3d, edits);
config_3d = r'F:\PigeonSuperModel_local\DeepLabCutModels\StopSignal-PigeonSuperModel-2022-07-08\StopSignal-3D-2022-07-08-3d\config.yaml'
Extract New Frames¶
# select videos for primary camera
video_sets = deeplabcut.auxiliaryfunctions.read_config(config_path)['video_sets']
primary_camera = deeplabcut.auxiliaryfunctions.read_config(config_3d)['camera_names'][0]
videos_primary = [str(video) for video in video_sets if primary_camera in video]
# change the number of frames to extract
edits = {'numframes2pick': 10}
# write changes to config.yaml
deeplabcut.auxiliaryfunctions.edit_config(config_path, edits);
# Extract frames
# Note: videos_list parameter is only available since dlc==2.2.1
deeplabcut.extract_frames(config_path, mode="automatic", algo="uniform", userfeedback = False, videos_list = videos_primary)
Optional: Extract Matching Frames in 3D projects¶
def patch_mp4toavi(config):
import cv2
import shutil
# get videos from config.yaml
video_sets = deeplabcut.auxiliaryfunctions.read_config(config)['video_sets'].keys()
projectpath = deeplabcut.auxiliaryfunctions.read_config(config)['project_path']
original_videos = [video for video in video_sets]
# patch videos for matched frame extraction se here: https://github.com/DeepLabCut/DeepLabCut/issues/1211#issuecomment-1177537516
patched_videos = [os.path.join(projectpath, 'videos', os.path.basename(video).split('.')[0] + '.avi') for video in original_videos]
# change video extension and move
for old, new in zip(original_videos, patched_videos):
# test and close videos
cap = cv2.VideoCapture(video)
while(cap.isOpened()):
# read first frame
cap.read()
# break after first frame
break
cap.release()
# Closes all the frames
cv2.destroyAllWindows()
# rename
if os.path.isfile(new):
print(f'File {new} already exists')
else:
shutil.move(old, new)
#os.rename(old, new)
print(f'Video: {old} changed to {new}')
return original_videos, patched_videos
def patch_reverse_mp4toavi(original_videos, patched_videos):
import shutil
for old, new in zip(original_videos, patched_videos):
# reverse video patch to original
shutil.move(new, old)
print(f'Video: {new} reversed to {old}')
return
# patch videos for matched frames see here: https://github.com/DeepLabCut/DeepLabCut/issues/1211#issuecomment-1177537516
original_videos, patched_videos = patch_mp4toavi(config_path)
# Overwrite matched synchronous frames with mode="match" and config3d file
deeplabcut.extract_frames(config_path, mode="match", config3d=config_3d, extracted_cam=0, userfeedback = False)
# reverse patched videos
patch_reverse_mp4toavi(original_videos, patched_videos)
Label your own Data first¶
# manually label frames
deeplabcut.label_frames(config_path)
# check labels
deeplabcut.check_labels(config_path)
Merge the PigeonSuperModel DataSet¶
How to use the PigeonSuperModel Dataset. Once you have your own DeepLabCut project for single Pigeon tracking, why not spiking your dataset with an extra 1000 pre-labeled frames? The cells below will guide you through this process.
# make a single function
def merge_psm_dataset(config):
import urllib.request
import tarfile
import pandas as pd
from pathlib import Path
# read config.yaml
target_dir = deeplabcut.auxiliaryfunctions.read_config(config)['project_path']
scorer = deeplabcut.auxiliaryfunctions.read_config(config)['scorer']
original_video_sets = deeplabcut.auxiliaryfunctions.read_config(config)['video_sets']
# download
print('Downloading DataSet from: gitlab.ruhr-uni-bochum.de/ikn/pigeonsupermodel ...')
dataset_url = 'https://gitlab.ruhr-uni-bochum.de/ikn/pigeonsupermodel/-/archive/main/pigeonsupermodel-main.tar.gz?path=dataset/DeepLabCut/labeled-data'
filename, _ = urllib.request.urlretrieve(dataset_url)
statement = 'The PigeonSuperModel Dataset was added to this project to increase the number of labeled frames. (C) PigeonSuperModel.com'
# Extract .tar
print(f'Extracting tar file to: {target_dir} ...')
with tarfile.open(filename, mode="r:gz") as tar:
tar.extractall(target_dir)
dataset_path = [os.path.join(target_dir, path) for path in os.listdir(target_dir) if 'pigeonsupermodel' in path][0]
# scrap pre-labeled dataset
print(f'Merging Dataset to: {os.path.join(target_dir, "labeled-data")} ...')
for root, dirs, files in os.walk(dataset_path):
for f in files:
if '.h5' in f:
h5file = os.path.join(root, f)
# rename scorer in .h5
df = pd.read_hdf(h5file)
df.columns = df.columns.set_levels([scorer], level=0)
# rename .h5 file
rename = os.path.join(root, f'CollectedData_{scorer}.h5')
df.to_hdf(rename, key = "df_with_missing", mode="w")
# move frame set to labeled-data
src = root
dst = os.path.join(target_dir, 'labeled-data', os.path.basename(root))
os.rename(src, dst)
# add dummy videopaths to config.yaml files
print('Adding video paths to "video_sets" in config.yaml ...')
framesets = os.listdir(os.path.join(target_dir, 'labeled-data'))
videos = [os.path.join(target_dir, 'videos',frameset+'.avi') for frameset in framesets]
# create new dict for video_sets
video_sets = {}
for video in videos:
rel_video_path = str(Path.resolve(Path(video)))
video_sets[rel_video_path] = {"crop": ", ".join(map(str, [0, 1920, 0, 1080]))} # how bad is this?
# expand existing video_sets
if original_video_sets is None:
new_video_sets = video_sets
else:
new_video_sets = {**original_video_sets, **video_sets}
# add video paths to project
edit = {'video_sets': new_video_sets,
'PigeonSuperModel_data': statement}
deeplabcut.auxiliaryfunctions.edit_config(config, edit);
print('The PigeonSuperModel Dataset has been successfully merged to your project! Enjoy')
return
merge_psm_dataset(config_path)
Now you are ready to create a training dataset and train your own model
Re-Train the PigeonSuperModel¶
# create training set
deeplabcut.create_training_dataset(config_path, num_shuffles=1, net_type='resnet_50', augmenter_type='imgaug')
# find pose_cfg.yaml
for root, dirs, files in os.walk(deeplabcut.auxiliaryfunctions.read_config(config)['project_path']):
for file in files:
if 'pose_cfg.yaml' in file:
pose_cfg = os.path.join(root, file)
pose_cfg
# edit relevant training parameters
edits = {'multi_step': [[0.005, 10000], [0.02, 430000], [0.002, 730000], [0.001, 1030000]], # change this to train over 1M iterations
'init_weights': path_to_lastsnapshot, # change this to use pre-trained model
'max_input_size': 2000, # change this to largest frame dimension in dataset
'global_scale': 0.4, # change this to rescale frames to reduce GPU load
}
deeplabcut.auxiliaryfunctions.edit_config(pose_cfg, edits)
# train
deeplabcut.train_network(config_path, max_snapshots_to_keep=None, displayiters=1000, saveiters=10000, maxiters=1000000, allow_growth=True)
Evaluate your new PigeonSuperModel¶
# change config.yaml file to evaluate all snapshots
edits = {'snapshotindex': 'all'}
deeplabcut.auxiliaryfunctions.edit_config(config_path, edits)
# evaluate snapshots
deeplabcut.evaluate_network(config_path, plotting=False)
def check_evaluation_results(config):
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# read config
config = deeplabcut.auxiliaryfunctions.read_config(config)
projectpath = config['project_path']
iteration = config['iteration']
modelname = config['Task']
pcutoff = config['pcutoff']
videores = list(config['video_sets'].items())[0][1]['crop'].split(',')[1:4:2]
# read evaluation results
evals = os.path.join(projectpath, 'evaluation-results', f'iteration-{iteration}', 'CombinedEvaluation-results.csv')
df = pd.read_csv(evals)
evaluation_results = df.loc[:, ~df.columns.isin(['Unnamed: 0', '%Training dataset', 'Shuffle number', 'p-cutoff used'])]
evaluation_results
# plot evaluation results
evaluation_results.plot.line(x = 'Training iterations:',
title = f"Evaluation results of DLC Model {modelname}",
ylabel = f"Pixel Error in {videores[0]}:{videores[1]} resolution frames",
xlabel = f"Training Iterations in DLC [p-cutoff = {pcutoff}]",
xticks = np.arange(0, 1000001, 40000),
rot = 45, figsize = (15,10))
plt.ticklabel_format(axis='both', style='plain')
plt.show()
# check evaluation results
return evaluation_results
# show evaluation results
check_evaluation_results(config_path)
# export model
deeplabcut.export_model(config_path, shuffle=0, iteration=0, snapshotindex=-1, make_tar=True, modelprefix='')
Contribute to PigeonSuperModel.com¶
Do you have an improved version of the PigeonSuperModel?
Share your labeled data and your new trained model with us to contribute to the PigeonSuperModel.com.
Get in touch at Guillermo.HidalgoGadea@ruhr-uni-bochum.de or at GuillermoHidalgoGadea.com