Skip to content

Commit 4e0a800

Browse files
committed
update bmn for table ennis projects PaddlePaddle#299
1 parent 76a5a8a commit 4e0a800

File tree

7 files changed

+388
-6
lines changed

7 files changed

+388
-6
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
MODEL: #MODEL field
2+
framework: "BMNLocalizer"
3+
backbone:
4+
name: "BMN"
5+
feat_dim: 2048
6+
tscale: 200
7+
dscale: 200
8+
prop_boundary_ratio: 0.5
9+
num_sample: 32
10+
num_sample_perbin: 3
11+
loss:
12+
name: "BMNLoss"
13+
tscale: 200
14+
dscale: 200
15+
16+
DATASET: #DATASET field
17+
batch_size: 4 #single card bacth size
18+
test_batch_size: 1
19+
num_workers: 8
20+
train:
21+
format: "BMNDataset"
22+
file_path: "/home/aistudio/work/BMN/Input_for_bmn/label_fixed.json"
23+
subset: "train"
24+
valid:
25+
format: "BMNDataset"
26+
file_path: "/home/aistudio/work/BMN/Input_for_bmn/label_fixed.json"
27+
subset: "validation"
28+
test:
29+
format: "BMNDataset"
30+
test_mode: True
31+
file_path: "/home/aistudio/work/BMN/Input_for_bmn/label_fixed.json"
32+
subset: "validation"
33+
34+
PIPELINE: #PIPELINE field
35+
train: #Mandotary, indicate the pipeline to deal with the training data
36+
load_feat:
37+
name: "LoadFeat"
38+
feat_path: "/home/aistudio/work/BMN/Input_for_bmn/feature"
39+
transform: #Mandotary, image transfrom operator
40+
- GetMatchMap:
41+
tscale: 200
42+
- GetVideoLabel:
43+
tscale: 200
44+
dscale: 200
45+
46+
valid: #Mandotary, indicate the pipeline to deal with the training data
47+
load_feat:
48+
name: "LoadFeat"
49+
feat_path: "/home/aistudio/work/BMN/Input_for_bmn/feature"
50+
transform: #Mandotary, image transfrom operator
51+
- GetMatchMap:
52+
tscale: 200
53+
- GetVideoLabel:
54+
tscale: 200
55+
dscale: 200
56+
57+
test: #Mandatory, indicate the pipeline to deal with the validing data
58+
load_feat:
59+
name: "LoadFeat"
60+
feat_path: "/home/aistudio/work/BMN/Input_for_bmn/feature"
61+
transform: #Mandotary, image transfrom operator
62+
- GetMatchMap:
63+
tscale: 200
64+
- GetVideoLabel:
65+
tscale: 200
66+
dscale: 200
67+
68+
OPTIMIZER: #OPTIMIZER field
69+
name: 'Adam'
70+
learning_rate:
71+
iter_step: True
72+
name: 'CustomPiecewiseDecay'
73+
boundaries: [4200]
74+
values: [0.001, 0.0001]
75+
weight_decay:
76+
name: 'L2'
77+
value: 1e-4
78+
79+
METRIC:
80+
name: 'BMNMetric'
81+
tscale: 200
82+
dscale: 200
83+
file_path: "/home/aistudio/work/BMN/Input_for_bmn/label_fixed.json"
84+
ground_truth_filename: "/home/aistudio/work/BMN/Input_for_bmn/label_gts.json"
85+
subset: "validation"
86+
output_path: "data/bmn/BMN_Test_output"
87+
result_path: "data/bmn/BMN_Test_results"
88+
89+
model_name: BMN
90+
epochs: 20 # Mandatory, total epoch
91+
log_level: "INFO"
92+
resume_from: "" #checkpoint path.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import copy
2+
import json
3+
import re
4+
import os
5+
6+
url = '/home/aistudio/work/BMN/Input_for_bmn/feature/'
7+
directory = os.fsencode(url)
8+
count = 0
9+
target_set = []
10+
11+
for file in os.listdir(directory):
12+
filename = os.fsdecode(file)
13+
target_name = filename.split('.npy')[0]
14+
target_set.append(target_name)
15+
count += 1
16+
print('Feature size:', len(target_set))
17+
18+
with open('/home/aistudio/work/BMN/Input_for_bmn/label.json') as f:
19+
data = json.load(f)
20+
21+
delet_set = []
22+
for key in data.keys():
23+
if not key in target_set:
24+
delet_set.append(key)
25+
26+
print('(Label) Original size:', len(data))
27+
print('(Label) Deleted size:', len(delet_set))
28+
29+
for item in delet_set:
30+
data.pop(item, None)
31+
32+
print('(Label) Fixed size:', len(data))
33+
34+
jsonString = json.dumps(data, indent=4, ensure_ascii=False)
35+
jsonFile = open('/home/aistudio/work/BMN/Input_for_bmn/label_fixed.json', 'w')
36+
jsonFile.write(jsonString)
37+
jsonFile.close()
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
"""
2+
get instance for bmn
3+
使用winds=8的滑窗,将所有子窗口的长度之和小于winds的进行合并
4+
合并后,父窗口代表bmn训练数据,子窗口代表tsn训练数据
5+
"""
6+
import os
7+
import sys
8+
import json
9+
import random
10+
import pickle
11+
import numpy as np
12+
import math
13+
14+
# for table tennis
15+
bmn_window = 8
16+
dataset = "/home/aistudio/work/BMN/"
17+
feat_dir = dataset + '/Features_example'
18+
out_dir = dataset + '/Input_for_bmn'
19+
label_files = {
20+
'train': 'label_cls14_small_train.json',
21+
'validation': 'label_cls14_small_test.json'
22+
}
23+
24+
global fps
25+
26+
27+
def gen_gts_for_bmn(gts_data):
28+
"""
29+
@param, gts_data, original gts for action detection
30+
@return, gts_bmn, output gts dict for bmn
31+
"""
32+
fps = gts_data['fps']
33+
gts_bmn = {'fps': fps, 'gts': []}
34+
for sub_item in gts_data['gts']:
35+
url = sub_item['url']
36+
37+
max_length = sub_item['total_frames']
38+
39+
gts_bmn['gts'].append({
40+
'url': url,
41+
'total_frames': max_length,
42+
'root_actions': []
43+
})
44+
sub_actions = sub_item['actions']
45+
# 跳过没有动作的片段
46+
if len(sub_actions) == 0:
47+
continue
48+
# duration > bmn_window, 动作持续时间大于bmn_windows,直接删除
49+
for idx, sub_action in enumerate(sub_actions):
50+
if sub_action['end_id'] - sub_action['start_id'] > bmn_window:
51+
sub_actions.pop(idx)
52+
53+
# 【滑动窗口,把每一个视频里的动作片段提取出来】
54+
root_actions = [sub_actions[0]]
55+
# before_id, 前一动作的最后一帧
56+
# after_id, 后一动作的第一帧
57+
before_id = 0
58+
for idx in range(1, len(sub_actions)):
59+
cur_action = sub_actions[idx]
60+
duration = (cur_action['end_id'] - root_actions[0]['start_id'])
61+
if duration > bmn_window: # windows只能包住一个动作就包,包不住就包多个
62+
after_id = cur_action['start_id']
63+
gts_bmn['gts'][-1]['root_actions'].append({
64+
'before_id':
65+
before_id,
66+
'after_id':
67+
after_id,
68+
'actions':
69+
root_actions
70+
})
71+
before_id = root_actions[-1]['end_id'] #更新滑窗
72+
root_actions = [cur_action]
73+
else:
74+
root_actions.append(cur_action)
75+
if idx == len(sub_actions) - 1:
76+
after_id = max_length
77+
gts_bmn['gts'][-1]['root_actions'].append({
78+
'before_id':
79+
before_id,
80+
'after_id':
81+
after_id,
82+
'actions':
83+
root_actions
84+
})
85+
86+
return gts_bmn
87+
88+
89+
def combile_gts(gts_bmn, gts_process, mode):
90+
"""
91+
1、bmn_window 范围内只有一个动作,只取一个目标框
92+
2、bmn_window 范围内有多个动作,取三个目标框(第一个动作、最后一个动作、所有动作)
93+
"""
94+
global fps
95+
fps = gts_process['fps']
96+
duration_second = bmn_window * 1.0
97+
duration_frame = bmn_window * fps
98+
feature_frame = duration_frame
99+
for item in gts_process['gts']:
100+
url = item['url']
101+
basename = os.path.basename(url).split('.')[0]
102+
root_actions = item['root_actions']
103+
# 把每一个视频里的动作片段提取出来
104+
for root_action in root_actions:
105+
segments = []
106+
# all actions
107+
segments.append({
108+
'actions': root_action['actions'],
109+
'before_id': root_action['before_id'],
110+
'after_id': root_action['after_id']
111+
})
112+
if len(root_action['actions']) > 1: #如果有多个动作,则第一个动作和最后一个动作,额外添加一次
113+
# first action
114+
segments.append({
115+
'actions': [root_action['actions'][0]],
116+
'before_id':
117+
root_action['before_id'],
118+
'after_id':
119+
root_action['actions'][1]['start_id']
120+
})
121+
# last action
122+
segments.append({
123+
'actions': [root_action['actions'][-1]],
124+
'before_id':
125+
root_action['actions'][-2]['end_id'],
126+
'after_id':
127+
root_action['after_id']
128+
})
129+
130+
# 把动作片段处理成window size大小,以适配BMN输入
131+
for segment in segments:
132+
before_id = segment['before_id']
133+
after_id = segment['after_id']
134+
actions = segment['actions']
135+
# before_id到after_id太长了,从里面取window_size帧,要先确定一个起始点,然后动作都要包住
136+
box0 = max(actions[-1]['end_id'] - bmn_window,
137+
before_id) #确定起始点
138+
box1 = min(actions[0]['start_id'],
139+
after_id - bmn_window) #确实起始点
140+
if box0 <= box1: # 一次检查
141+
if int(box0) - int(box1) == 0:
142+
cur_start = box0
143+
else:
144+
box0 = math.ceil(box0)
145+
box1 = int(box1)
146+
cur_start = random.randint(box0, box1)
147+
cur_end = cur_start + bmn_window
148+
cur_start = round(cur_start, 2)
149+
cur_end = round(cur_end, 2)
150+
name = '{}_{}_{}'.format(basename, cur_start, cur_end)
151+
annotations = []
152+
for action in actions:
153+
label = str(1.0 * action['label_ids'][0])
154+
label_name = action['label_names'][0]
155+
seg0 = 1.0 * round((action['start_id'] - cur_start),
156+
2) #存储的是到开始位置(时间: s)的距离
157+
seg1 = 1.0 * round((action['end_id'] - cur_start), 2)
158+
annotations.append({
159+
'segment': [seg0, seg1],
160+
'label': label,
161+
'label_name': label_name
162+
})
163+
gts_bmn[name] = {
164+
'duration_second': duration_second,
165+
'duration_frame': duration_frame,
166+
'feature_frame': feature_frame,
167+
'subset': mode,
168+
'annotations': annotations
169+
}
170+
171+
return gts_bmn
172+
173+
174+
def save_feature_to_numpy(gts_bmn, folder):
175+
global fps
176+
print('save feature for bmn ...')
177+
if not os.path.exists(folder):
178+
os.mkdir(folder)
179+
process_gts_bmn = {}
180+
miss = 0
181+
for item, value in gts_bmn.items():
182+
# split to rsplit 针对文件命名修改
183+
basename, start_id, end_id = item.rsplit('_', 2)
184+
if not basename in process_gts_bmn:
185+
process_gts_bmn[basename] = []
186+
process_gts_bmn[basename].append({
187+
'name': item,
188+
'start': float(start_id),
189+
'end': float(end_id)
190+
})
191+
for item, values in process_gts_bmn.items():
192+
feat_path = os.path.join(feat_dir, item + '.pkl')
193+
feature_video = pickle.load(open(feat_path, 'rb'))['image_feature']
194+
for value in values:
195+
save_cut_name = os.path.join(folder, value['name'])
196+
a, b, c = save_cut_name.rsplit('_', 2)
197+
if float(b) > 360:
198+
print(b)
199+
start_frame = round(value['start'] * fps)
200+
end_frame = round(value['end'] * fps)
201+
if end_frame > len(feature_video):
202+
miss += 1
203+
continue
204+
feature_cut = [
205+
feature_video[i] for i in range(start_frame, end_frame)
206+
]
207+
np_feature_cut = np.array(feature_cut, dtype=np.float32)
208+
np.save(save_cut_name, np_feature_cut)
209+
210+
print('miss number (broken sample):', miss)
211+
212+
213+
if __name__ == "__main__":
214+
if not os.path.exists(out_dir):
215+
os.mkdir(out_dir)
216+
gts_bmn = {}
217+
for item, value in label_files.items():
218+
label_file = os.path.join(dataset, value)
219+
gts_data = json.load(open(label_file, 'rb'))
220+
gts_process = gen_gts_for_bmn(gts_data)
221+
gts_bmn = combile_gts(gts_bmn, gts_process, item)
222+
223+
with open(out_dir + '/label.json', 'w', encoding='utf-8') as f:
224+
data = json.dumps(gts_bmn, indent=4, ensure_ascii=False)
225+
f.write(data)
226+
227+
save_feature_to_numpy(gts_bmn, out_dir + '/feature')
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import json
2+
3+
with open('/home/aistudio/work/BMN/Input_for_bmn/label_fixed.json') as f:
4+
data = json.load(f)
5+
f.close()
6+
7+
target_format = {'taxonomy': None, 'database': data, 'version': None}
8+
9+
jsonString = json.dumps(target_format, indent=4, ensure_ascii=False)
10+
jsonFile = open('/home/aistudio/work/BMN/Input_for_bmn/label_gts.json', 'w')
11+
jsonFile.write(jsonString)
12+
jsonFile.close()

paddlevideo/metrics/ActivityNet/anet_prop.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ def evaluate(self):
164164

165165
if self.verbose:
166166
print('[RESULTS] Performance on ActivityNet proposal task.')
167+
with open("data/bmn/BMN_Test_results/auc_result.txt",
168+
"a") as text_file:
169+
text_file.write(
170+
'\tArea Under the AR vs AN curve: {}% \n'.format(
171+
100. * float(area_under_curve) /
172+
proposals_per_video[-1]))
167173
print('\tArea Under the AR vs AN curve: {}%'.format(
168174
100. * float(area_under_curve) / proposals_per_video[-1]))
169175

@@ -282,7 +288,7 @@ def average_recall_vs_avg_nr_proposals(self,
282288
true_positives_tiou = score >= tiou
283289
# Get number of proposals as a percentage of total retrieved.
284290
pcn_proposals = np.minimum(
285-
(score.shape[1] * pcn_lst).astype(np.int), score.shape[1])
291+
(score.shape[1] * pcn_lst).astype(int), score.shape[1])
286292

287293
for j, nr_proposals in enumerate(pcn_proposals):
288294
# Compute the number of matches for each percentage of the proposals

0 commit comments

Comments
 (0)