support CCPG

This commit is contained in:
darkliang
2023-06-27 21:25:38 +08:00
parent 9ff2520c05
commit 78171dba91
6 changed files with 625 additions and 2 deletions
+104
View File
@@ -0,0 +1,104 @@
data_cfg:
dataset_name: CCPG
dataset_root: your_path
dataset_partition: ./datasets/CCPG/CCPG.json
num_workers: 1
data_in_use: [True,False,False,False]
remove_no_gallery: false # Remove probe if no gallery for it
test_dataset_name: CCPG
evaluator_cfg:
enable_float16: true
restore_ckpt_strict: true
restore_hint: 60000
save_name: GaitBase
eval_func: evaluate_CCPG
sampler:
batch_shuffle: false
batch_size: 16
sample_type: all_ordered # all indicates whole sequence used to test, while ordered means input sequence by its natural order; Other options: fixed_unordered
frames_all_limit: 720 # limit the number of sampled frames to prevent out of memory
metric: euc # cos
transform:
- type: BaseSilCuttingTransform
img_w: 64
loss_cfg:
- loss_term_weight: 1.0
margin: 0.2
type: TripletLoss
log_prefix: triplet
- loss_term_weight: 1.0
scale: 16
type: CrossEntropyLoss
log_prefix: softmax
log_accuracy: true
model_cfg:
model: Baseline
backbone_cfg:
type: ResNet9
block: BasicBlock
channels: # Layers configuration for automatically model construction
- 64
- 128
- 256
- 512
layers:
- 1
- 1
- 1
- 1
strides:
- 1
- 2
- 2
- 1
maxpool: false
SeparateFCs:
in_channels: 512
out_channels: 256
parts_num: 16
SeparateBNNecks:
class_num: 100
in_channels: 256
parts_num: 16
bin_num:
- 16
optimizer_cfg:
lr: 0.1
momentum: 0.9
solver: SGD
weight_decay: 0.0005
scheduler_cfg:
gamma: 0.1
milestones: # Learning Rate Reduction at each milestones
- 20000
- 40000
- 50000
scheduler: MultiStepLR
trainer_cfg:
enable_float16: true # half_percesion float for memory reduction and speedup
fix_BN: false
with_test: false
log_iter: 100
restore_ckpt_strict: true
restore_hint: 0
save_iter: 60000
save_name: GaitBase
sync_BN: true
total_iter: 60000
sampler:
batch_shuffle: true
batch_size:
- 8 # TripletSampler, batch_size[0] indicates Number of Identity
- 16 # batch_size[1] indicates Samples sequqnce for each Identity
frames_num_fixed: 30 # fixed frames number for training
frames_num_max: 50 # max frames number for unfixed training
frames_num_min: 10 # min frames number for unfixed traing
sample_type: fixed_unordered # fixed control input frames number, unordered for controlling order of input tensor; Other options: unfixed_ordered or all_ordered
type: TripletSampler
transform:
- type: BaseSilCuttingTransform
img_w: 64
+206
View File
@@ -0,0 +1,206 @@
{
"TRAIN_SET": [
"000",
"001",
"002",
"003",
"004",
"005",
"006",
"007",
"008",
"009",
"010",
"011",
"012",
"013",
"014",
"015",
"016",
"017",
"018",
"019",
"020",
"021",
"022",
"023",
"024",
"025",
"026",
"027",
"028",
"029",
"030",
"031",
"032",
"033",
"034",
"035",
"036",
"037",
"038",
"039",
"040",
"041",
"042",
"043",
"044",
"045",
"046",
"047",
"048",
"049",
"050",
"051",
"052",
"053",
"054",
"055",
"056",
"057",
"058",
"059",
"060",
"061",
"062",
"063",
"064",
"065",
"066",
"067",
"068",
"069",
"070",
"071",
"072",
"073",
"074",
"075",
"076",
"077",
"078",
"079",
"080",
"081",
"082",
"083",
"084",
"085",
"086",
"087",
"088",
"089",
"090",
"091",
"092",
"093",
"094",
"095",
"096",
"097",
"098",
"099"
],
"TEST_SET": [
"100",
"101",
"102",
"103",
"104",
"105",
"106",
"107",
"108",
"109",
"110",
"111",
"112",
"113",
"114",
"115",
"116",
"117",
"118",
"119",
"120",
"121",
"122",
"123",
"124",
"125",
"126",
"127",
"128",
"129",
"130",
"131",
"132",
"133",
"134",
"135",
"136",
"137",
"138",
"139",
"140",
"141",
"142",
"143",
"144",
"145",
"146",
"147",
"148",
"149",
"150",
"151",
"152",
"153",
"154",
"155",
"156",
"157",
"158",
"159",
"160",
"161",
"162",
"163",
"164",
"165",
"166",
"167",
"168",
"169",
"170",
"171",
"172",
"173",
"174",
"175",
"176",
"177",
"178",
"179",
"180",
"181",
"182",
"183",
"184",
"185",
"186",
"187",
"188",
"189",
"190",
"191",
"192",
"193",
"194",
"195",
"196",
"197",
"198",
"199"
]
}
+27
View File
@@ -0,0 +1,27 @@
# The CCPG Benchmark
A Cloth-Changing Benchmark for Person re-identification and Gait Recognition (CCPG).
The original dataset can be found [here](https://github.com/BNU-IVC/CCPG). The original dataset is not publicly available. You need to request access to the dataset in order to download it.
## Data Pretreatment
```python
python datasets/CCPG/organize_ccpg.py --sil_path 'CCPG/CCPG_D_MASK_FACE_SHOE' --rgb_path 'CCPG/CCPG_G_SIL' --output_path 'CCPG/CCPG-end2end-pkl'
```
## Train
### GatiBase model:
`CUDA_VISIBLE_DEVICES=0,1,2,3 python -m torch.distributed.launch --nproc_per_node=4 opengait/main.py --cfgs ./configs/gaitbase/gaitbase_ccpg.yaml --phase train`
## Citation
If you use this dataset in your research, please cite the following paper:
```
@InProceedings{Li_2023_CVPR,
author = {Li, Weijia and Hou, Saihui and Zhang, Chunjie and Cao, Chunshui and Liu, Xu and Huang, Yongzhen and Zhao, Yao},
title = {An In-Depth Exploration of Person Re-Identification and Gait Recognition in Cloth-Changing Conditions},
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
month = {June},
year = {2023},
pages = {13824-13833}
}
```
+101
View File
@@ -0,0 +1,101 @@
import os
import pickle
import numpy as np
import cv2
from tqdm import tqdm
import argparse
T_W = 64
T_H = 64
def cut_img(img):
# A silhouette contains too little white pixels
# might be not valid for identification.
# Get the top and bottom point
y = img.sum(axis=1)
y_top = (y != 0).argmax(axis=0)
y_btm = (y != 0).cumsum(axis=0).argmax(axis=0)
img = img[y_top:y_btm + 1, :]
# As the height of a person is larger than the width,
# use the height to calculate resize ratio.
_r = img.shape[1] / img.shape[0]
_t_w = int(T_H * _r)
img = cv2.resize(img, (_t_w, T_H), interpolation=cv2.INTER_AREA)
# Get the median of x axis and regard it as the x center of the person.
sum_point = img.sum()
sum_column = img.sum(axis=0).cumsum()
x_center = -1
for i in range(sum_column.size):
if sum_column[i] > sum_point / 2:
x_center = i
break
if x_center < 0:
return None
h_T_W = int(T_W / 2)
left = x_center - h_T_W
right = x_center + h_T_W
if left <= 0 or right >= img.shape[1]:
left += h_T_W
right += h_T_W
_ = np.zeros((img.shape[0], h_T_W))
img = np.concatenate([_, img, _], axis=1)
img = img[:, left:right]
return img.astype('uint8')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='CCPG dataset Preprocessing.')
parser.add_argument('--sil_path', default='', type=str,
help='Root path of raw silhouette dataset.')
parser.add_argument('--rgb_path', default='', type=str,
help='Root path of raw RGB dataset.')
parser.add_argument('-o', '--output_path', default='',
type=str, help='Output path of pickled dataset.')
args = parser.parse_args()
RGB_SIZE = (128, 128)
for _id in tqdm(sorted(os.listdir(args.sil_path))):
for _type in sorted(os.listdir(os.path.join(args.rgb_path, _id))):
for _view in sorted(os.listdir(os.path.join(args.rgb_path, _id, _type))):
imgs = []
segs = []
ratios = []
aligned_segs = []
for img_file in sorted(os.listdir(os.path.join(args.rgb_path, _id, _type, _view))):
seg_file = img_file.split(".")[0]+".png"
img_path = os.path.join(
args.rgb_path, _id, _type, _view, img_file)
seg_path = os.path.join(
args.rgb_path, _id, _type, _view, seg_file)
if not os.path.exists(seg_path):
print("Not Found: "+seg_path)
continue
img = cv2.imread(img_path)
seg = cv2.imread(seg_path, cv2.IMREAD_GRAYSCALE)
ratio = img.shape[1]/img.shape[0]
aligned_seg = cut_img(seg)
img = np.transpose(cv2.cvtColor(cv2.resize(
img, RGB_SIZE), cv2.COLOR_BGR2RGB), (2, 0, 1))
imgs.append(img)
segs.append(cv2.resize(
seg, RGB_SIZE))
aligned_segs.append(aligned_seg)
ratios.append(ratio)
if len(imgs) > 0:
output_path = os.path.join(
args.output_path, _id, _type, _view)
os.makedirs(output_path, exist_ok=True)
pickle.dump(np.asarray(imgs), open(os.path.join(
output_path, _view+"-rgbs.pkl"), "wb"))
pickle.dump(np.asarray(segs), open(os.path.join(
output_path, _view+"-sils.pkl"), "wb"))
pickle.dump(np.asarray(ratios), open(os.path.join(
output_path, _view+"-ratios.pkl"), "wb"))
pickle.dump(np.asarray(aligned_segs), open(os.path.join(
output_path, _view+"-aligned-sils.pkl"), "wb"))
else:
print("No imgs Found: " +
os.path.join(args.rgb_path, _id, _type, _view))
continue
+124 -2
View File
@@ -3,7 +3,7 @@ from time import strftime, localtime
import numpy as np
from utils import get_msg_mgr, mkdir
from .metric import mean_iou, cuda_dist, compute_ACC_mAP, evaluate_rank
from .metric import mean_iou, cuda_dist, compute_ACC_mAP, evaluate_rank, evaluate_many
from .re_rank import re_ranking
@@ -71,7 +71,7 @@ def cross_view_gallery_evaluation(feature, label, seq_type, view, dataset, metri
def single_view_gallery_evaluation(feature, label, seq_type, view, dataset, metric):
probe_seq_dict = {'CASIA-B': {'NM': ['nm-05', 'nm-06'], 'BG': ['bg-01', 'bg-02'], 'CL': ['cl-01', 'cl-02']},
'OUMVLP': {'NM': ['00']},
'CASIA-E': {'NM': ['H-scene2-nm-1', 'H-scene2-nm-2', 'L-scene2-nm-1', 'L-scene2-nm-2', 'H-scene3-nm-1', 'H-scene3-nm-2', 'L-scene3-nm-1', 'L-scene3-nm-2', 'H-scene3_s-nm-1', 'H-scene3_s-nm-2', 'L-scene3_s-nm-1', 'L-scene3_s-nm-2',],
'CASIA-E': {'NM': ['H-scene2-nm-1', 'H-scene2-nm-2', 'L-scene2-nm-1', 'L-scene2-nm-2', 'H-scene3-nm-1', 'H-scene3-nm-2', 'L-scene3-nm-1', 'L-scene3-nm-2', 'H-scene3_s-nm-1', 'H-scene3_s-nm-2', 'L-scene3_s-nm-1', 'L-scene3_s-nm-2', ],
'BG': ['H-scene2-bg-1', 'H-scene2-bg-2', 'L-scene2-bg-1', 'L-scene2-bg-2', 'H-scene3-bg-1', 'H-scene3-bg-2', 'L-scene3-bg-1', 'L-scene3-bg-2', 'H-scene3_s-bg-1', 'H-scene3_s-bg-2', 'L-scene3_s-bg-1', 'L-scene3_s-bg-2'],
'CL': ['H-scene2-cl-1', 'H-scene2-cl-2', 'L-scene2-cl-1', 'L-scene2-cl-2', 'H-scene3-cl-1', 'H-scene3-cl-2', 'L-scene3-cl-1', 'L-scene3-cl-2', 'H-scene3_s-cl-1', 'H-scene3_s-cl-2', 'L-scene3_s-cl-1', 'L-scene3_s-cl-2']
}
@@ -280,3 +280,125 @@ def evaluate_Gait3D(data, dataset, metric='euc'):
# print_csv_format(dataset_name, results)
msg_mgr.log_info(results)
return results
def evaluate_CCPG(data, dataset, metric='euc'):
msg_mgr = get_msg_mgr()
feature, label, seq_type, view = data['embeddings'], data['labels'], data['types'], data['views']
label = np.array(label)
for i in range(len(view)):
view[i] = view[i].split("_")[0]
view_np = np.array(view)
view_list = list(set(view))
view_list.sort()
view_num = len(view_list)
probe_seq_dict = {'CCPG': [["U0_D0_BG", "U0_D0"], [
"U3_D3"], ["U1_D0"], ["U0_D0_BG"]]}
gallery_seq_dict = {
'CCPG': [["U1_D1", "U2_D2", "U3_D3"], ["U0_D3"], ["U1_D1"], ["U0_D0"]]}
if dataset not in (probe_seq_dict or gallery_seq_dict):
raise KeyError("DataSet %s hasn't been supported !" % dataset)
num_rank = 5
acc = np.zeros([len(probe_seq_dict[dataset]),
view_num, view_num, num_rank]) - 1.
ap_save = []
cmc_save = []
minp = []
for (p, probe_seq) in enumerate(probe_seq_dict[dataset]):
# for gallery_seq in gallery_seq_dict[dataset]:
gallery_seq = gallery_seq_dict[dataset][p]
gseq_mask = np.isin(seq_type, gallery_seq)
gallery_x = feature[gseq_mask, :]
# print("gallery_x", gallery_x.shape)
gallery_y = label[gseq_mask]
gallery_view = view_np[gseq_mask]
pseq_mask = np.isin(seq_type, probe_seq)
probe_x = feature[pseq_mask, :]
probe_y = label[pseq_mask]
probe_view = view_np[pseq_mask]
msg_mgr.log_info(
("gallery length", len(gallery_y), gallery_seq, "probe length", len(probe_y), probe_seq))
distmat = cuda_dist(probe_x, gallery_x, metric).cpu().numpy()
# cmc, ap = evaluate(distmat, probe_y, gallery_y, probe_view, gallery_view)
cmc, ap, inp = evaluate_many(
distmat, probe_y, gallery_y, probe_view, gallery_view)
ap_save.append(ap)
cmc_save.append(cmc[0])
minp.append(inp)
# print(ap_save, cmc_save)
msg_mgr.log_info(
'===Rank-1 (Exclude identical-view cases for Person Re-Identification)===')
msg_mgr.log_info('CL: %.3f,\tUP: %.3f,\tDN: %.3f,\tBG: %.3f' % (
cmc_save[0]*100, cmc_save[1]*100, cmc_save[2]*100, cmc_save[3]*100))
msg_mgr.log_info(
'===mAP (Exclude identical-view cases for Person Re-Identification)===')
msg_mgr.log_info('CL: %.3f,\tUP: %.3f,\tDN: %.3f,\tBG: %.3f' % (
ap_save[0]*100, ap_save[1]*100, ap_save[2]*100, ap_save[3]*100))
msg_mgr.log_info(
'===mINP (Exclude identical-view cases for Person Re-Identification)===')
msg_mgr.log_info('CL: %.3f,\tUP: %.3f,\tDN: %.3f,\tBG: %.3f' %
(minp[0]*100, minp[1]*100, minp[2]*100, minp[3]*100))
for (p, probe_seq) in enumerate(probe_seq_dict[dataset]):
# for gallery_seq in gallery_seq_dict[dataset]:
gallery_seq = gallery_seq_dict[dataset][p]
for (v1, probe_view) in enumerate(view_list):
for (v2, gallery_view) in enumerate(view_list):
gseq_mask = np.isin(seq_type, gallery_seq) & np.isin(
view, [gallery_view])
gallery_x = feature[gseq_mask, :]
gallery_y = label[gseq_mask]
pseq_mask = np.isin(seq_type, probe_seq) & np.isin(
view, [probe_view])
probe_x = feature[pseq_mask, :]
probe_y = label[pseq_mask]
dist = cuda_dist(probe_x, gallery_x, metric)
idx = dist.sort(1)[1].cpu().numpy()
# print(p, v1, v2, "\n")
acc[p, v1, v2, :] = np.round(
np.sum(np.cumsum(np.reshape(probe_y, [-1, 1]) == gallery_y[idx[:, 0:num_rank]], 1) > 0,
0) * 100 / dist.shape[0], 2)
result_dict = {}
for i in range(1):
msg_mgr.log_info(
'===Rank-%d (Include identical-view cases)===' % (i + 1))
msg_mgr.log_info('CL: %.3f,\tUP: %.3f,\tDN: %.3f,\tBG: %.3f' % (
np.mean(acc[0, :, :, i]),
np.mean(acc[1, :, :, i]),
np.mean(acc[2, :, :, i]),
np.mean(acc[3, :, :, i])))
for i in range(1):
msg_mgr.log_info(
'===Rank-%d (Exclude identical-view cases)===' % (i + 1))
msg_mgr.log_info('CL: %.3f,\tUP: %.3f,\tDN: %.3f,\tBG: %.3f' % (
de_diag(acc[0, :, :, i]),
de_diag(acc[1, :, :, i]),
de_diag(acc[2, :, :, i]),
de_diag(acc[3, :, :, i])))
result_dict["scalar/test_accuracy/CL"] = acc[0, :, :, i]
result_dict["scalar/test_accuracy/UP"] = acc[1, :, :, i]
result_dict["scalar/test_accuracy/DN"] = acc[2, :, :, i]
result_dict["scalar/test_accuracy/BG"] = acc[3, :, :, i]
np.set_printoptions(precision=2, floatmode='fixed')
for i in range(1):
msg_mgr.log_info(
'===Rank-%d of each angle (Exclude identical-view cases)===' % (i + 1))
msg_mgr.log_info('CL: {}'.format(de_diag(acc[0, :, :, i], True)))
msg_mgr.log_info('UP: {}'.format(de_diag(acc[1, :, :, i], True)))
msg_mgr.log_info('DN: {}'.format(de_diag(acc[2, :, :, i], True)))
msg_mgr.log_info('BG: {}'.format(de_diag(acc[3, :, :, i], True)))
return result_dict
+63
View File
@@ -156,3 +156,66 @@ def evaluate_rank(distmat, p_lbls, g_lbls, max_rank=50):
all_cmc = all_cmc.sum(0) / num_valid_p
return all_cmc, all_AP, all_INP
def evaluate_many(distmat, q_pids, g_pids, q_camids, g_camids, max_rank=50):
num_q, num_g = distmat.shape
if num_g < max_rank:
max_rank = num_g
print("Note: number of gallery samples is quite small, got {}".format(num_g))
indices = np.argsort(distmat, axis=1) # 对应位置变成从小到大的序号
matches = (g_pids[indices] == q_pids[:, np.newaxis]).astype(
np.int32) # 根据indices调整顺序 g_pids[indices]
# print(matches)
# compute cmc curve for each query
all_cmc = []
all_AP = []
all_INP = []
num_valid_q = 0.
for q_idx in range(num_q):
# get query pid and camid
q_pid = q_pids[q_idx]
q_camid = q_camids[q_idx]
# remove gallery samples that have the same pid and camid with query
order = indices[q_idx]
remove = (g_pids[order] == q_pid) & (g_camids[order] == q_camid)
keep = np.invert(remove)
# compute cmc curve
# binary vector, positions with value 1 are correct matches
orig_cmc = matches[q_idx][keep]
if not np.any(orig_cmc):
# this condition is true when query identity does not appear in gallery
continue
cmc = orig_cmc.cumsum()
pos_idx = np.where(orig_cmc == 1)
max_pos_idx = np.max(pos_idx)
inp = cmc[max_pos_idx] / (max_pos_idx + 1.0)
all_INP.append(inp)
cmc[cmc > 1] = 1
all_cmc.append(cmc[:max_rank])
num_valid_q += 1.
# compute average precision
# reference: https://en.wikipedia.org/wiki/Evaluation_measures_(information_retrieval)#Average_precision
num_rel = orig_cmc.sum()
tmp_cmc = orig_cmc.cumsum()
tmp_cmc = [x / (i+1.) for i, x in enumerate(tmp_cmc)]
tmp_cmc = np.asarray(tmp_cmc) * orig_cmc
AP = tmp_cmc.sum() / num_rel
all_AP.append(AP)
assert num_valid_q > 0, "Error: all query identities do not appear in gallery"
all_cmc = np.asarray(all_cmc).astype(np.float32)
all_cmc = all_cmc.sum(0) / num_valid_q
mAP = np.mean(all_AP)
mINP = np.mean(all_INP)
return all_cmc, mAP, mINP