Skip to content

Commit

Permalink
first Sports2D commit
Browse files Browse the repository at this point in the history
  • Loading branch information
davidpagnon committed Apr 28, 2023
0 parents commit 03eabdc
Show file tree
Hide file tree
Showing 17 changed files with 2,330 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**/__pycache__/
*.pyc
*.egg-info/
dist/
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BSD 3-Clause License

Copyright (c) 2022, perfanalytics
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Right click in folder parent to Sports2D -> Open PowerShell here

conda create -n Sports2D python[version='<3.11,>=3.7']

pip install ipython toml numpy pandas scipy anytree opencv-python mediapipe PyQt5
ipython

from Sports2D import Sports2D
Sports2D.detect_pose('Sports2D\Demo\Config_demo.toml')
Sports2D.compute_angles('Sports2D\Demo\Config_demo.toml')


Copy, edit, and if you like, rename your Config_demo.toml file
73 changes: 73 additions & 0 deletions Sports2D/Demo/Config_demo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
###############################################################################
## SPORTS2D PROJECT PARAMETERS ##
###############################################################################

# Configure your project parameters here


[project]
video_dir = '' # BETWEEN SINGLE QUOTES! # If empty, project dir is current dir
video_file = 'demo.mp4'
frame_rate = 30 #Hz


[pose]
pose_algo = 'BLAZEPOSE' # 'OPENPOSE' or 'BLAZEPOSE'
# OpenPose is more accurate and supports multi-person detection, but needs to be installed separately
# Coming soon: 'deeplabcut', 'alphapose'

[pose.BLAZEPOSE]
# 0,1,2. 2 is slightly slower but more accurate
model_complexity = 2

[pose.OPENPOSE]
# Install OpenPose from https://github.com/CMU-Perceptual-Computing-Lab/openpose
#BODY_25 is standard, BODY_25B is more accurate but requires downloading the model from
# https://github.com/CMU-Perceptual-Computing-Lab/openpose_train/blob/master/experimental_models/README.md
openpose_model = 'BODY_25'
# Installation path of openpose (between single quotes)
openpose_path = 'D:\softs\openpose-1.6.0-binaries-win64-gpu-flir-3d_recommended\openpose'


[compute_angles]
joint_angles = ['rankle', 'lankle', 'rknee', 'lknee', 'rhip', 'lhip', 'rshoulder', 'lshoulder', 'relbow', 'lelbow']
# select among ['rankle', 'lankle', 'rknee', 'lknee', 'rhip', 'lhip', 'rshoulder', 'lshoulder', 'relbow', 'lelbow']
segment_angles = ['rfoot', 'lfoot', 'rshank', 'lshank', 'rthigh', 'lthigh', 'trunk', 'rarm', 'larm', 'rforearm', 'lforearm']
# select among ['rfoot', 'lfoot', 'rshank', 'lshank', 'rthigh', 'lthigh', 'trunk', 'rarm', 'larm', 'rforearm', 'lforearm']



# ADVANCED CONFIGURATION

[pose_advanced] # only for OPENPOSE
load_pose = true # else proceed to detection
save_vid = true
save_img = true
interp_gap_smaller_than = 5 # do not interpolate bigger gaps
show_plots = false
filter = true
filter_type = 'butterworth' # butterworth, gaussian, LOESS, median
[pose_advanced.butterworth]
order = 4
cut_off_frequency = 6 # Hz
[pose_advanced.gaussian]
sigma_kernel = 1 #px
[pose_advanced.loess]
nb_values_used = 5 # = fraction of data used * nb frames
[pose_advanced.median]
kernel_size = 3


[compute_angles_advanced] # for OPENPOSE and BLAZEPOSE
show_plots = false
filter = true
filter_type = 'butterworth' # butterworth, gaussian, LOESS, median
[compute_angles_advanced.butterworth]
order = 4
cut_off_frequency = 6 # Hz
[compute_angles_advanced.gaussian]
sigma_kernel = 1 #px
[compute_angles_advanced.loess]
nb_values_used = 5 # = fraction of data used * nb frames
[compute_angles_advanced.median]
kernel_size = 3
Binary file added Sports2D/Demo/demo.mp4
Binary file not shown.
197 changes: 197 additions & 0 deletions Sports2D/Sports2D.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-


'''
##############################################################
## SPORTS2D ##
##############################################################
This repository provides a workflow to compute 2D markerless
joint and segment angles from videos.
These angles can be plotted and processed with any
spreadsheet software or programming language.
This is a headless version, but apps will be released
for Windows, Linux, MacOS, as well as Android and iOS.
Mobile versions will only support exploratory joint detection
from BlazePose, hence less accurately and tunable.
If you need to detect several persons and want more accurate results,
you can install and use OpenPose:
https://github.com/CMU-Perceptual-Computing-Lab/openpose
-----
Sports2D installation:
-----
Optional:
- Install Miniconda
- Open a Anaconda Prompt and type:
`conda create -n Sports2D python>=3.7`
`conda activate Sports2D`
pip install
- Open a python prompt and type `pip install sports2d`
- `pip show sports2d`
- Adjust your settings (in particular video path) in `Config_demo.toml`
- ```from Sports2D import Sports2D
Sports2D.detect_pose('Sports2D\Demo\Config_demo.toml')
Sports2D.compute_angles('Sports2D\Demo\Config_demo.toml')```
-----
/!\ Warning /!\
-----
- The angle estimation is only as good as the pose estimation algorithm, i.e., it is not perfect.
- It will only lead to acceptable results if the persons move in the 2D plane (sagittal plane).
- The persons need to be filmed as perpendicularly as possible from their side.
If you need research-grade markerless joint kinematics, consider using several cameras,
and constraining angles to a biomechanically accurate model. See Pose2Sim for example:
https://github.com/perfanalytics/pose2sim
-----
Pose detection:
-----
Detect joint centers from a video with OpenPose or BlazePose.
Save a 2D csv position file per person, and optionally json files, image files, and video files.
If OpenPose is used, multiple persons can be consistently detected across frames.
Interpolates sequences of missing data if they are less than N frames long.
Optionally filters results with Butterworth, gaussian, median, or loess filter.
Optionally displays figures.
If BlazePose is used, only one person can be detected.
No interpolation nor filtering options available. Not plotting available.
-----
Angle computation:
-----
Compute joint and segment angles from csv position files.
Automatically adjust angles when person switches to face the other way.
Save a 2D csv angle file per person.
Optionally filters results with Butterworth, gaussian, median, or loess filter.
Optionally displays figures.
Joint angle conventions:
- Ankle dorsiflexion: Between heel and big toe, and ankle and knee
- Knee flexion: Between hip, knee, and ankle
- Hip flexion: Between knee, hip, and shoulder
- Shoulder flexion: Between hip, shoulder, and elbow
- Elbow flexion: Between wrist, elbow, and shoulder
Segment angle conventions:
Angles are measured anticlockwise between the horizontal and the segment.
- Foot: Between heel and big toe
- Shank: Between ankle and knee
- Thigh: Between hip and knee
- Arm: Between shoulder and elbow
- Forearm: Between elbow and wrist
- Trunk: Between hip midpoint and shoulder midpoint
-----
To-do list:
-----
- GUI applications for all platforms (with Kivy: https://kivy.org/)
- Pose refinement: click and move badly estimated 2D points (cf DeepLabCut: https://www.youtube.com/watch?v=bEuBKB7eqmk)
- Include OpenPose in Sports2D (dockerize it cf https://github.com/stanfordnmbl/mobile-gaitlab/blob/master/demo/Dockerfile)
- Constrain points to OpenSim skeletal model for better angle estimation (cf Pose2Sim but in 2D https://github.com/perfanalytics/pose2sim)
'''


## INIT
import toml
import os
from pathlib import Path
import time
import logging, logging.handlers

with open(Path('logs.txt'), 'a+') as log_f: pass
logging.basicConfig(format='%(message)s', level=logging.INFO,
handlers = [logging.handlers.TimedRotatingFileHandler(Path('logs.txt'), when='D', interval=7), logging.StreamHandler()])


## AUTHORSHIP INFORMATION
__author__ = "David Pagnon"
__copyright__ = "Copyright 2023, Sports2D"
__credits__ = ["David Pagnon"]
__license__ = "BSD 3-Clause License"
__version__ = "0.1"
__maintainer__ = "David Pagnon"
__email__ = "[email protected]"
__status__ = "Development"


## FUNCTIONS
def read_config_file(config):
'''
Read configation file.
'''

config_dict = toml.load(config)
return config_dict


def base_params(config_dict):
'''
Retrieve sequence name and frames to be analyzed.
'''

video_dir = Path(config_dict.get('project').get('video_dir')).resolve()
if video_dir == '': video_dir = os.getcwd()
video_file = Path(config_dict.get('project').get('video_file'))
frame_rate = config_dict.get('project').get('frame_rate')

return video_dir, video_file, frame_rate


def detect_pose(config='Config_demo.toml'):
'''
Compute 2D pose from video.
Save 2D csv file, and optionally json files, image files, and video file.
Optionally interpolates missing data, filters them, and displays figures.
'''

from Sports2D.detect_pose import detect_pose_fun

config_dict = read_config_file(config)
_, video_file, _ = base_params(config_dict)

logging.info("\n\n---------------------------------------------------------------------")
logging.info(f"Detect pose for video {video_file}")
logging.info("---------------------------------------------------------------------")
start = time.time()

detect_pose_fun(config_dict)

end = time.time()
logging.info(f'Pose detection took {end-start:.2f} s.')


def compute_angles(config='Config_demo.toml'):
'''
Compute joint and segment angles from 2D points coordinates in csv file.
Save csv file.
Optionally interpolates missing data, filters them, and displays figures.
'''

from Sports2D.compute_angles import compute_angles_fun

config_dict = read_config_file(config)
_, video_file, _ = base_params(config_dict)
joint_angles = config_dict.get('compute_angles').get('joint_angles')
segment_angles = config_dict.get('compute_angles').get('segment_angles')

logging.info("\n\n---------------------------------------------------------------------")
logging.info(f"Compute angles for video {video_file} ")
logging.info(f"for {joint_angles}")
logging.info(f"and {segment_angles}.")
logging.info("---------------------------------------------------------------------")
start = time.time()

compute_angles_fun(config_dict)

end = time.time()
logging.info(f'Joint and segment computation took {end-start:.2f} s.')



Loading

0 comments on commit 03eabdc

Please sign in to comment.