forked from wzmsltw/BSN-boundary-sensitive-network.pytorch
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpgm.py
executable file
·418 lines (355 loc) · 16.4 KB
/
pgm.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
"""
Example run:
python main.py --module PGM --do_representation --num_videoframes 25 --tem_results_dir /checkpoint/cinjon/spaceofmotion/bsn/teminf/101.2019.8.30-00101.1 --pgm_proposals_dir /checkpoint/cinjon/spaceofmotion/bsn/pgmprops --video_anno /private/home/cinjon/Code/BSN-boundary-sensitive-network.pytorch/data/gymnastics_annotations/anno_fps12.on.json --video_info /private/home/cinjon/Code/BSN-boundary-sensitive-network.pytorch/data/gymnastics_annotations/video_info_new.csv --pgm_score_threshold 0.25
"""
# -*- coding: utf-8 -*-
import json
import os
import time
import numpy as np
import pandas as pd
import torch.multiprocessing as mp
from scipy.interpolate import interp1d
def load_json(file):
with open(file) as json_file:
data = json.load(json_file)
return data
def iou_with_anchors(anchors_min, anchors_max, box_min, box_max):
"""Compute jaccard score between a box and the anchors.
"""
len_anchors = anchors_max - anchors_min
int_xmin = np.maximum(anchors_min, box_min)
int_xmax = np.minimum(anchors_max, box_max)
inter_len = np.maximum(int_xmax - int_xmin, 0.)
union_len = len_anchors - inter_len + box_max - box_min
jaccard = np.divide(inter_len, union_len)
return jaccard
def ioa_with_anchors(anchors_min, anchors_max, box_min, box_max):
"""Compute intersection between score a box and the anchors.
"""
len_anchors = anchors_max - anchors_min
int_xmin = np.maximum(anchors_min, box_min)
int_xmax = np.minimum(anchors_max, box_max)
inter_len = np.maximum(int_xmax - int_xmin, 0.)
scores = np.divide(inter_len, len_anchors)
return scores
def generate_proposals(opt, video_list, video_data):
start_time = time.time()
# NOTE: Hackity hack hack.
video_dict = None
anno_df = None
if type(video_data) == dict:
video_dict = video_data
else:
anno_df = video_data
tem_results_dir = opt['tem_results_dir']
proposals_dir = opt['pgm_proposals_dir']
skipped_paths = []
no_props_paths = []
print(anno_df, flush=True)
print(video_list, flush=True)
for video_name in video_list:
print('Starting %s' % video_name)
results_path = os.path.join(tem_results_dir, '%s.csv' % video_name)
if not os.path.exists(results_path):
print('Skipping %s because %s is not a path.' % (video_name, results_path))
skipped_paths.append(results_path)
continue
anno_df_ = anno_df[anno_df.video == video_name]
tdf = pd.read_csv(results_path)
start_scores = tdf.start.values[:]
end_scores = tdf.end.values[:]
try:
frame_list = tdf.frames.values[:]
except Exception as e:
frame_list = tdf.frame.values[:]
start_bins = np.zeros(len(start_scores))
start_bins[[0, -1]] = 1
for idx in range(1, len(start_scores) - 1):
if start_scores[idx] > start_scores[
idx + 1] and start_scores[idx] > start_scores[idx - 1]:
if start_scores[idx] > 0.9:
start_bins[[idx, idx - 1, idx + 1]] = 1
else:
start_bins[idx] = 1
end_bins = np.zeros(len(end_scores))
end_bins[[0, -1]] = 1
for idx in range(1, len(start_scores) - 1):
if end_scores[idx] > end_scores[
idx + 1] and end_scores[idx] > end_scores[idx - 1]:
if end_scores[idx] > 0.9:
end_bins[[idx, idx - 1, idx + 1]] = 1
else:
end_bins[idx] = 1
xmin_list = []
xmin_score_list = []
xmax_list = []
xmax_score_list = []
for index in range(len(start_scores)):
if start_bins[index] == 1:
xmin_list.append(int(frame_list[index]))
xmin_score_list.append(start_scores[index])
if end_bins[index] == 1:
xmax_list.append(int(frame_list[index]))
xmax_score_list.append(end_scores[index])
# if len(xmax_list) == 0:
# print("***\nNO MAX LIST: ", results_path, "\n***")
# raise
new_props = []
# NOTE!!!
threshold = 500 if opt['dataset'] == 'activitynet' else 300
for ii in range(len(xmax_list)):
if ii % 5000 == 0:
print('Done %d / %d' % (ii, len(xmax_list)))
tmp_xmax = xmax_list[ii]
tmp_xmax_score = xmax_score_list[ii]
for ij in range(len(xmin_list)):
tmp_xmin = xmin_list[ij]
tmp_xmin_score = xmin_score_list[ij]
if tmp_xmax - tmp_xmin < 10:
break
if tmp_xmax - tmp_xmin > threshold:
continue
new_props.append(
[tmp_xmin, tmp_xmax, tmp_xmin_score, tmp_xmax_score])
col_name = ["xmin", "xmax", "xmin_score", "xmax_score", "match_iou", "match_ioa", "match_xmin", "match_xmax"]
path = os.path.join(proposals_dir, '%s.proposals.csv' % video_name)
try:
new_props = np.stack(new_props)
except Exception as e:
no_props_paths.append(results_path)
new_df = pd.DataFrame(columns=col_name)
new_df.to_csv(path, index=False)
continue
new_df = pd.DataFrame(new_props, columns=col_name[:4])
# new_df["score"] = new_df.xmin_score * new_df.xmax_score
# print('Filtering from ', len(new_df))
# new_df = new_df[new_df.score > opt['pgm_score_threshold']]
# print('... To ', len(new_df))
# path = os.path.join(proposals_dir, '%s.proposals.csv' % video_name)
# print('saving preliminary to %s' % path)
# new_df.to_csv(path, index=False)
if video_dict is not None:
video_info = video_dict[video_name]
video_fps = video_info['fps']
annos = video_info["annotations"]
gt_xmins = []
gt_xmaxs = []
for idx in range(len(annos)):
if annos[idx]['label'] == 'off':
continue
gt_xmins.append(annos[idx]["segment"][0] * video_fps)
gt_xmaxs.append(annos[idx]["segment"][1] * video_fps)
elif anno_df_ is not None:
gt_xmins = anno_df_.startFrame.values[:]
gt_xmaxs = anno_df_.endFrame.values[:]
# Ok, so all of these gt_xmins and gt_xmaxs are the same ...
# ... As are the xmin and xmax values in the DFs.
new_iou_list = []
match_xmin_list = []
match_xmax_list = []
for j in range(len(new_df)):
tmp_new_iou = list(
iou_with_anchors(
new_df.xmin.values[j],
new_df.xmax.values[j],
gt_xmins,
gt_xmaxs)
)
new_iou_list.append(max(tmp_new_iou))
match_xmin_list.append(gt_xmins[tmp_new_iou.index(max(tmp_new_iou))])
match_xmax_list.append(gt_xmaxs[tmp_new_iou.index(max(tmp_new_iou))])
new_ioa_list = []
for j in range(len(new_df)):
tmp_new_ioa = max(
ioa_with_anchors(new_df.xmin.values[j],
new_df.xmax.values[j], gt_xmins, gt_xmaxs))
new_ioa_list.append(tmp_new_ioa)
new_df["match_iou"] = new_iou_list
new_df["match_ioa"] = new_ioa_list
new_df["match_xmin"] = match_xmin_list
new_df["match_xmax"] = match_xmax_list
print('saving to %s' % path)
new_df.to_csv(path, index=False)
print('Video %s took %.4f time' % (video_name, time.time() - start_time))
print('Skipped path len: ', len(skipped_paths), ', NoProps path len: ', len(no_props_paths))
print('Total time was %.4f' % (time.time() - start_time), skipped_paths, no_props_paths)
def getDatasetDict(opt):
print(opt['video_info'])
print(opt['video_anno'])
video_info = opt['video_info']
if opt['dataset'] != 'activitynet':
video_info = os.path.join(video_info, 'Full_Annotation.csv')
print(video_info)
df = pd.read_csv(video_info)
database = load_json(opt["video_anno"])
video_dict = {}
keys = ['duration_frame', 'duration_second', 'feature_frame', 'annotations', 'fps']
for i in range(len(df)):
video_name = df.video.values[i]
video_info = database[video_name]
video_new_info = {k: video_info[k] for k in keys}
# if 'thumos' in opt['dataset']:
# video_new_info['subset'] = video_name.split('_')[1]
# else:
# video_new_info['subset'] = df.subset.values[i]
video_dict[video_name] = video_new_info
return video_dict
def bookend_zeros(arr, num):
return np.concatenate([np.zeros([num]), arr, np.zeros([num])])
def generate_features(opt, video_list, video_dict):
num_sample_start = opt["num_sample_start"]
num_sample_end = opt["num_sample_end"]
num_sample_action = opt["num_sample_action"]
num_videoframes = opt["num_videoframes"]
skip_videoframes = opt["skip_videoframes"]
bookend_num = int(num_videoframes / skip_videoframes)
normalizer = skip_videoframes*(bookend_num - 1) - (skip_videoframes + int((skip_videoframes + 1)/2))
tem_results_dir = opt['tem_results_dir']
proposals_dir = opt['pgm_proposals_dir']
features_dir = opt['pgm_features_dir']
start_time = time.time()
success_count = 0
not_exist_feats = 0
not_exist_props = 0
for video_name in video_list:
s0 = time.time()
tem_path = os.path.join(tem_results_dir, video_name + ".csv")
print(tem_path)
if not os.path.exists(tem_path):
print("NOT generating features for %s because features don't exist." % video_name)
not_exist_feats += 1
continue
adf = pd.read_csv(tem_path)
try:
adf_frames = adf.frames.values[:]
except Exception as e:
adf_frames = adf.frame.values[:]
proposals_path = os.path.join(proposals_dir, '%s.proposals.csv' % video_name)
if not os.path.exists(proposals_path):
print("NOT generating features for %s because proposals don't exist." % video_name)
not_exist_props += 1
continue
pdf = pd.read_csv(proposals_path)
print('Doing %s with paths %s and %s' % (video_name, tem_path, proposals_path))
score_action = bookend_zeros(adf.action.values[:], bookend_num)
score_end = bookend_zeros(adf.end.values[:], bookend_num)
score_start = bookend_zeros(adf.start.values[:], bookend_num)
#
snippets = [skip_videoframes*i - normalizer for i in range(bookend_num)] + list(adf_frames) + [skip_videoframes*i + skip_videoframes + adf_frames[-1] for i in range(bookend_num)]
print('Computing the interp1ds')
f_action = interp1d(snippets, score_action, axis=0, fill_value='extrapolate')
f_start = interp1d(snippets, score_start, axis=0, fill_value='extrapolate')
f_end = interp1d(snippets, score_end, axis=0, fill_value='extrapolate')
print('Done ciomputing interp1ds')
feature_bsp = []
s1 = time.time()
for idx in range(len(pdf)):
if idx % 1000 == 0 and idx > 0:
print('At %d of %d. S/S: %.4f' % (idx, len(pdf), idx / (time.time() - s1)))
xmin = pdf.xmin.values[idx]
xmax = pdf.xmax.values[idx]
xlen = xmax - xmin
xmin_0 = xmin - xlen * opt["bsp_boundary_ratio"]
xmin_1 = xmin + xlen * opt["bsp_boundary_ratio"]
xmax_0 = xmax - xlen * opt["bsp_boundary_ratio"]
xmax_1 = xmax + xlen * opt["bsp_boundary_ratio"]
#start
plen_start = (xmin_1 - xmin_0) / (num_sample_start - 1)
tmp_x_new = [xmin_0 + plen_start * ii for ii in range(num_sample_start)]
tmp_y_new_start = np.concatenate((
f_action(tmp_x_new),
f_start(tmp_x_new)
))
#end
plen_end = (xmax_1 - xmax_0) / (num_sample_end - 1)
tmp_x_new = [xmin_0 + plen_end * ii for ii in range(num_sample_end)]
tmp_y_new_end = np.concatenate((f_action(tmp_x_new),
f_end(tmp_x_new)))
#action
plen_action = (xmax - xmin) / (num_sample_action - 1)
# I originall had the following (see xmin_0)
# tmp_x_new = [xmin_0 + plen_action * ii for ii in range(num_sample_action)]
# But they have this:
tmp_x_new = [xmin + plen_action * ii for ii in range(num_sample_action)]
tmp_y_new_action = f_action(tmp_x_new)
tmp_y_new_action = np.reshape(tmp_y_new_action, [-1])
# make the feature bsp
feature_bsp.append(np.concatenate(
[tmp_y_new_action, tmp_y_new_start, tmp_y_new_end]))
feature_bsp = np.array(feature_bsp)
path = os.path.join(
features_dir, "%s.features.npy" % video_name)
print("Size of feature_bsp: ", feature_bsp.shape, len(adf), len(pdf), video_name)
np.save(path, feature_bsp)
print('Time from start to finish for video with adf len %d and pdf len %d was %.4f.' % (len(adf), len(pdf), time.time() - s0))
success_count += 1
print('Total time was ', time.time() - start_time, success_count, not_exist_feats, not_exist_props)
def PGM_proposal_generation(opt):
if opt['dataset'] == 'activitynet':
video_data = pd.read_csv(opt["video_info"])
else:
video_data = pd.read_csv(os.path.join(opt["video_info"], 'Full_Annotation.csv'))
video_list = sorted(list(set(video_data.video.values[:])))
# video_list = video_list[:100]
# video_list = [v for v in video_list if v.startswith('v_3V')]
num_videos = len(video_list)
num_threads = min(num_videos, opt['pgm_thread'])
num_videos_per_thread = int(num_videos / num_threads)
processes = []
func = generate_proposals
for tid in range(num_threads - 1):
tmp_video_list = video_list[tid * num_videos_per_thread:(tid + 1) * num_videos_per_thread]
p = mp.Process(target=func,
args=(
opt,
tmp_video_list,
video_data,
))
p.start()
processes.append(p)
tmp_video_list = video_list[(num_threads - 1) *
num_videos_per_thread:]
p = mp.Process(target=func,
args=(
opt,
tmp_video_list,
video_data,
))
p.start()
processes.append(p)
for p in processes:
p.join()
def PGM_feature_generation(opt):
video_dict = getDatasetDict(opt)
video_list = sorted(video_dict.keys())
# NOTE: change this back.
# video_list = [k for k in video_list if '12.18.18' in k or '12.5.18' in k]
func = generate_features
num_videos = len(video_list)
num_threads = min(num_videos, opt['pgm_thread'])
num_videos_per_thread = int(num_videos / opt["pgm_thread"])
processes = []
for tid in range(opt["pgm_thread"] - 1):
tmp_video_list = video_list[tid * num_videos_per_thread:(tid + 1) *
num_videos_per_thread]
p = mp.Process(target=func,
args=(
opt,
tmp_video_list,
video_dict,
))
p.start()
processes.append(p)
tmp_video_list = video_list[(opt["pgm_thread"] - 1) *
num_videos_per_thread:]
p = mp.Process(target=func,
args=(
opt,
tmp_video_list,
video_dict,
))
p.start()
processes.append(p)
for p in processes:
p.join()