Skip to content

Commit d2eeeca

Browse files
committed
Initial commit
1 parent 214cfb2 commit d2eeeca

44 files changed

Lines changed: 5547 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
pip-wheel-metadata/
24+
share/python-wheels/
25+
*.egg-info/
26+
.installed.cfg
27+
*.egg
28+
MANIFEST
29+
30+
# Installer logs
31+
pip-log.txt
32+
pip-delete-this-directory.txt
33+
34+
# Jupyter Notebook
35+
.ipynb_checkpoints
36+
37+
# IPython
38+
profile_default/
39+
ipython_config.py
40+
41+
# Environments
42+
.env
43+
.venv
44+
env/
45+
venv/
46+
ENV/
47+
env.bak/
48+
venv.bak/
49+
50+
# Project Specific
51+
models/*.weights
52+
models/*.pth
53+
data/*
54+
data/*.csv

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Torben Teepe
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,27 @@ This repository contains the PyTorch code for:
33
GaitGraph: Graph Convolutional Network for Skeleton-Based Gait Recognition
44
[Torben Teepe](https://github.com/tteepe), Ali Khan, Johannes Gilg, [Fabian Herzog](https://github.com/fubel)
55

6+
![Pipeline](images/pipeline.png)
7+
8+
## Quick Start
9+
Quick Start & models coming soon!
10+
11+
## Main Results
12+
Top-1 Accuracy per probe angle excluding identical-view cases for the provided models on
13+
[CASIA-B](http://www.cbsr.ia.ac.cn/english/Gait%20Databases.asp) dataset.
14+
15+
| | 0 | 18 | 36 | 54 | 72 | 90 | 108 | 126 | 144 | 162 | 180 | mean |
16+
|:-------|-----:|-----:|-----:|-----:|-----:|-----:|------:|------:|------:|------:|------:|-------:|
17+
| NM#5-6 | 85.3 | 88.5 | 91 | 92.5 | 87.2 | 86.5 | 88.4 | 89.2 | 87.9 | 85.9 | 81.9 | 87.7 |
18+
| BG#1-2 | 75.8 | 76.7 | 75.9 | 76.1 | 71.4 | 73.9 | 78 | 74.7 | 75.4 | 75.4 | 69.2 | 74.8 |
19+
| CL#1-2 | 69.6 | 66.1 | 68.8 | 67.2 | 64.5 | 62 | 69.5 | 65.6 | 65.7 | 66.1 | 64.3 | 66.3 |
20+
21+
## Licence & Acknowledgement
22+
GaitPose itself is released under the MIT License (see LICENSE).
23+
24+
The following parts of the code are borrowed from other projects. Thanks for their wonderful work!
25+
- Object Detector: [eriklindernoren/PyTorch-YOLOv3](https://github.com/eriklindernoren/PyTorch-YOLOv3)
26+
- Pose Estimator: [HRNet/HRNet-Human-Pose-Estimation](https://github.com/HRNet/HRNet-Human-Pose-Estimation)
27+
- ST-GCN Model: [yysijie/st-gcn](https://github.com/yysijie/st-gcn)
28+
- ResGCNv1 Model: [yfsong0709/ResGCNv1](https://github.com/yfsong0709/ResGCNv1)
29+
- SupCon Loss: [HobbitLong/SupContrast](https://github.com/HobbitLong/SupContrast)

images/pipeline.png

235 KB
Loading

models/download_weights.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
# Download weights for vanilla YOLOv3
3+
wget -c https://pjreddie.com/media/files/yolov3.weights
4+
# Download weights for tiny YOLOv3
5+
wget -c https://pjreddie.com/media/files/yolov3-tiny.weights
6+
## Download weights for backbone network
7+
#wget -c https://pjreddie.com/media/files/darknet53.conv.74
8+
9+
print "#############################################################"
10+
print "######## Weights for HRNet Pose Estimation need to ##########"
11+
print "######## be downloaded manually from here: ##########"
12+
print "######## https://drive.google.com/drive/folders/1nzM_OBV9LbAEA7HClC0chEyf_7ECDXYA"
13+
print "######## Files: pose_hrnet_*.pth ##########"
14+
print "#############################################################"

requirements.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
yacs==0.1.8
2+
numpy==1.19.5
3+
torch==1.7.1
4+
torchvision==0.8.2
5+
matplotlib==3.3.3
6+
tabulate==0.8.7
7+
tensorflow==2.4.0
8+
tensorboard==2.4.0
9+
pillow==8.1.0
10+
tqdm==4.56.0
11+
opencv-python~=4.5
12+
jupyter==1.0.0
13+
pandas==1.1.0

save/.gitkeep

Whitespace-only changes.

src/common.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import os
2+
import argparse
3+
import torch
4+
from models.st_gcn.st_gcn import STGCNEmbedding
5+
import models.ResGCNv1
6+
7+
8+
def parse_option():
9+
parser = argparse.ArgumentParser(description="Training model on gait sequence")
10+
parser.add_argument("dataset", choices=["casia-b", "outdoor-gait", "tum-gaid"])
11+
parser.add_argument("train_data_path", help="Path to train data CSV")
12+
parser.add_argument("--valid_data_path", help="Path to validation data CSV")
13+
parser.add_argument("--valid_split", type=float, default=0.2)
14+
15+
parser.add_argument("--checkpoint_path", help="Path to checkpoint to resume")
16+
parser.add_argument("--weight_path", help="Path to weights for model")
17+
18+
# Optionals
19+
parser.add_argument("--num_workers", type=int, default=8)
20+
parser.add_argument(
21+
"--gpus", default="0", help="-1 for CPU, use comma for multiple gpus"
22+
)
23+
parser.add_argument("--batch_size", type=int, default=64)
24+
parser.add_argument("--batch_size_validation", type=int, default=64)
25+
parser.add_argument("--epochs", type=int, default=500)
26+
parser.add_argument("--start_epoch", type=int, default=1)
27+
parser.add_argument("--log_interval", type=int, default=10)
28+
parser.add_argument("--save_interval", type=int, default=50, help="save frequency")
29+
parser.add_argument(
30+
"--save_best_start", type=float, default=0.3, help="save frequency"
31+
)
32+
parser.add_argument("--use_amp", action="store_true")
33+
parser.add_argument("--tune", action="store_true")
34+
parser.add_argument("--shuffle", action="store_true")
35+
parser.add_argument("--exp_name", help="Name of the experiment")
36+
37+
parser.add_argument("--network_name", default="resgcn-n39-r4")
38+
parser.add_argument("--sequence_length", type=int, default=60)
39+
parser.add_argument("--embedding_layer_size", type=int, default=256)
40+
parser.add_argument("--temporal_kernel_size", type=int, default=9)
41+
parser.add_argument("--dropout", type=float, default=0.4)
42+
parser.add_argument("--learning_rate", type=float, default=1e-3)
43+
parser.add_argument(
44+
"--lr_decay_rate", type=float, default=0.1, help="decay rate for learning rate"
45+
)
46+
parser.add_argument("--point_noise_std", type=float, default=0.05)
47+
parser.add_argument("--joint_noise_std", type=float, default=0.1)
48+
parser.add_argument("--flip_probability", type=float, default=0.5)
49+
parser.add_argument("--mirror_probability", type=float, default=0.5)
50+
parser.add_argument("--weight_decay", type=float, default=1e-5)
51+
parser.add_argument("--use_multi_branch", action="store_true")
52+
parser.add_argument(
53+
"--temp", type=float, default=0.07, help="temperature for loss function"
54+
)
55+
opt = parser.parse_args()
56+
57+
# Sanitize opts
58+
opt.gpus_str = opt.gpus
59+
opt.gpus = [int(gpu) for gpu in opt.gpus.split(",")]
60+
61+
return opt
62+
63+
64+
def log_hyperparameter(writer, opt, accuracy, loss):
65+
writer.add_hparams(
66+
{
67+
"batch_size": opt.batch_size,
68+
"sequence_length": opt.sequence_length,
69+
"embedding_layer_size": opt.embedding_layer_size,
70+
"dropout": opt.dropout,
71+
"learning_rate": opt.learning_rate,
72+
"lr_decay_rate": opt.lr_decay_rate,
73+
"point_noise_std": opt.point_noise_std,
74+
"weight_decay": opt.weight_decay,
75+
"temp": opt.temp,
76+
},
77+
{
78+
"hparam/accuracy": accuracy,
79+
"hparam/loss": loss,
80+
},
81+
)
82+
83+
84+
def setup_environment(opt):
85+
# HACK: Fix tensorboard
86+
import tensorflow as tf
87+
import tensorboard as tb
88+
89+
tf.io.gfile = tb.compat.tensorflow_stub.io.gfile
90+
91+
os.environ["CUDA_VISIBLE_DEVICES"] = opt.gpus_str
92+
opt.cuda = opt.gpus[0] >= 0
93+
torch.device("cuda" if opt.cuda else "cpu")
94+
95+
return opt
96+
97+
98+
def get_model_stgcn(opt):
99+
# Model
100+
input_channels = 3
101+
edge_importance_weighting = True
102+
graph_args = {"strategy": "spatial"}
103+
104+
embedding_net = STGCNEmbedding(
105+
input_channels,
106+
graph_args,
107+
edge_importance_weighting=edge_importance_weighting,
108+
embedding_layer_size=opt.embedding_layer_size,
109+
temporal_kernel_size=opt.temporal_kernel_size,
110+
dropout=opt.dropout,
111+
)
112+
113+
return embedding_net
114+
115+
116+
def get_model_resgcn(graph, opt):
117+
model_args = {
118+
"A": torch.tensor(graph.A, dtype=torch.float32, requires_grad=False),
119+
"num_class": opt.embedding_layer_size,
120+
"num_input": 1 if not opt.use_multi_branch else 3,
121+
"num_channel": 3 if not opt.use_multi_branch else 6,
122+
"parts": graph.parts,
123+
}
124+
return models.ResGCNv1.create(opt.network_name, **model_args)
125+
126+
127+
def get_trainer(model, opt, steps_per_epoch):
128+
optimizer = torch.optim.Adam(
129+
model.parameters(), lr=opt.learning_rate, weight_decay=opt.weight_decay
130+
)
131+
scheduler = torch.optim.lr_scheduler.OneCycleLR(
132+
optimizer, opt.learning_rate, epochs=opt.epochs, steps_per_epoch=steps_per_epoch
133+
)
134+
scaler = torch.cuda.amp.GradScaler(enabled=opt.use_amp)
135+
136+
return optimizer, scheduler, scaler
137+
138+
139+
def load_checkpoint(model, optimizer, scheduler, scaler, opt):
140+
if opt.checkpoint_path is not None:
141+
checkpoint = torch.load(opt.checkpoint_path)
142+
model.load_state_dict(checkpoint["model"])
143+
optimizer.load_state_dict(checkpoint["optimizer"])
144+
scheduler.load_state_dict(checkpoint["scheduler"])
145+
scaler.load_state_dict(checkpoint["scaler"])
146+
opt.start_epoch = checkpoint["epoch"]
147+
148+
if opt.weight_path is not None:
149+
checkpoint = torch.load(opt.weight_path)
150+
model.load_state_dict(checkpoint["model"], strict=False)
151+
152+
153+
def save_model(model, optimizer, scheduler, scaler, opt, epoch, save_file):
154+
print("==> Saving...")
155+
state = {
156+
"opt": opt,
157+
"model": model.state_dict(),
158+
"optimizer": optimizer.state_dict(),
159+
"scheduler": scheduler.state_dict(),
160+
"scaler": scaler.state_dict(),
161+
"epoch": epoch,
162+
}
163+
torch.save(state, save_file)
164+
del state
165+
166+
167+
def count_parameters(model):
168+
"""
169+
Useful function to compute number of parameters in a model.
170+
"""
171+
return sum(p.numel() for p in model.parameters() if p.requires_grad)

src/datasets/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
from .preparation import DatasetSimple, DatasetDetections
2+
from .gait import (
3+
CasiaBPose,
4+
)
5+
6+
7+
def dataset_factory(name):
8+
if name == "casia-b":
9+
return CasiaBPose
10+
11+
raise ValueError()

0 commit comments

Comments
 (0)