Skip to content

Commit 2be1954

Browse files
authored
Merge pull request #759 from int-brain-lab/litpose
Lightning Pose task
2 parents e65bedb + f3d3afd commit 2be1954

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

ibllib/pipes/video_tasks.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
import subprocess
3+
import time
34
import traceback
45
from pathlib import Path
56

@@ -610,3 +611,118 @@ def _run(self, overwrite=True, run_qc=True, plot_qc=True):
610611
self.status = -1
611612

612613
return output_files
614+
615+
616+
class LightningPose(base_tasks.VideoTask):
617+
# TODO: make one task per cam?
618+
gpu = 1
619+
io_charge = 100
620+
level = 2
621+
force = True
622+
job_size = 'large'
623+
624+
env = Path.home().joinpath('Documents', 'PYTHON', 'envs', 'litpose', 'bin', 'activate')
625+
scripts = Path.home().joinpath('Documents', 'PYTHON', 'iblscripts', 'deploy', 'serverpc', 'litpose')
626+
627+
@property
628+
def signature(self):
629+
signature = {
630+
'input_files': [(f'_iblrig_{cam}Camera.raw.mp4', self.device_collection, True) for cam in self.cameras],
631+
'output_files': [(f'_ibl_{cam}Camera.lightningPose.pqt', 'alf', True) for cam in self.cameras]
632+
}
633+
634+
return signature
635+
636+
@staticmethod
637+
def _video_intact(file_mp4):
638+
"""Checks that the downloaded video can be opened and is not empty"""
639+
cap = cv2.VideoCapture(str(file_mp4))
640+
frame_count = cap.get(cv2.CAP_PROP_FRAME_COUNT)
641+
intact = True if frame_count > 0 else False
642+
cap.release()
643+
return intact
644+
645+
def _check_env(self):
646+
"""Check that scripts are present, env can be activated and get iblvideo version"""
647+
assert len(list(self.scripts.rglob('run_litpose.*'))) == 2, \
648+
f'Scripts run_litpose.sh and run_litpose.py do not exist in {self.scripts}'
649+
assert self.env.exists(), f"environment does not exist in assumed location {self.env}"
650+
command2run = f"source {self.env}; python -c 'import iblvideo; print(iblvideo.__version__)'"
651+
process = subprocess.Popen(
652+
command2run,
653+
shell=True,
654+
stdout=subprocess.PIPE,
655+
stderr=subprocess.PIPE,
656+
executable="/bin/bash"
657+
)
658+
info, error = process.communicate()
659+
if process.returncode != 0:
660+
raise AssertionError(f"environment check failed\n{error.decode('utf-8')}")
661+
version = info.decode("utf-8").strip().split('\n')[-1]
662+
return version
663+
664+
def _run(self, overwrite=True, **kwargs):
665+
666+
# Gather video files
667+
self.session_path = Path(self.session_path)
668+
mp4_files = [
669+
self.session_path.joinpath(self.device_collection, f'_iblrig_{cam}Camera.raw.mp4') for cam in self.cameras
670+
if self.session_path.joinpath(self.device_collection, f'_iblrig_{cam}Camera.raw.mp4').exists()
671+
]
672+
673+
labels = [label_from_path(x) for x in mp4_files]
674+
_logger.info(f'Running on {labels} videos')
675+
676+
# Check the environment
677+
self.version = self._check_env()
678+
_logger.info(f'iblvideo version {self.version}')
679+
680+
# If all results exist and overwrite is False, skip computation
681+
expected_outputs_present, expected_outputs = self.assert_expected(self.output_files, silent=True)
682+
if overwrite is False and expected_outputs_present is True:
683+
actual_outputs = expected_outputs
684+
return actual_outputs
685+
686+
# Else, loop over videos
687+
actual_outputs = []
688+
for label, mp4_file in zip(labels, mp4_files):
689+
# Catch exceptions so that the other cams can still run but set status to Errored
690+
try:
691+
# Check that the GPU is (still) accessible
692+
check_nvidia_driver()
693+
# Check that the video can be loaded
694+
if not self._video_intact(mp4_file):
695+
_logger.error(f"Corrupt raw video file {mp4_file}")
696+
self.status = -1
697+
continue
698+
t0 = time.time()
699+
_logger.info(f'Running Lightning Pose on {label}Camera.')
700+
command2run = f"{self.scripts.joinpath('run_litpose.sh')} {str(self.env)} {mp4_file} {overwrite}"
701+
_logger.info(command2run)
702+
process = subprocess.Popen(
703+
command2run,
704+
shell=True,
705+
stdout=subprocess.PIPE,
706+
stderr=subprocess.PIPE,
707+
executable="/bin/bash",
708+
)
709+
info, error = process.communicate()
710+
if process.returncode != 0:
711+
error_str = error.decode("utf-8").strip()
712+
_logger.error(f'Lightning pose failed for {label}Camera.\n\n'
713+
f'++++++++ Output of subprocess for debugging ++++++++\n\n'
714+
f'{error_str}\n'
715+
f'++++++++++++++++++++++++++++++++++++++++++++\n')
716+
self.status = -1
717+
continue
718+
else:
719+
_logger.info(f'{label} camera took {(time.time() - t0)} seconds')
720+
result = next(self.session_path.joinpath('alf').glob(f'_ibl_{label}Camera.lightningPose*.pqt'))
721+
actual_outputs.append(result)
722+
723+
except BaseException:
724+
_logger.error(traceback.format_exc())
725+
self.status = -1
726+
continue
727+
728+
return actual_outputs

0 commit comments

Comments
 (0)