asatheesh commited on
Commit
31cd24b
·
verified ·
1 Parent(s): b5d5eac

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. TransRAC/.idea/.gitignore +8 -0
  2. TransRAC/.idea/SVIP_Counting.iml +8 -0
  3. TransRAC/.idea/deployment.xml +14 -0
  4. TransRAC/.idea/misc.xml +4 -0
  5. TransRAC/.idea/modules.xml +8 -0
  6. TransRAC/.idea/vcs.xml +6 -0
  7. TransRAC/dataset/RepCountA_Loader.py +104 -0
  8. TransRAC/dataset/RepCountA_raw_Loader.py +142 -0
  9. TransRAC/dataset/RepCountB_Loader.py +111 -0
  10. TransRAC/dataset/UCFRep_loader.py +113 -0
  11. TransRAC/dataset/__init__.py +0 -0
  12. TransRAC/dataset/label_norm.py +35 -0
  13. TransRAC/figures/readme.md +1 -0
  14. TransRAC/log/__init__.py +1 -0
  15. TransRAC/mmaction/__init__.py +15 -0
  16. TransRAC/mmaction/apis/__init__.py +8 -0
  17. TransRAC/mmaction/apis/inference.py +156 -0
  18. TransRAC/mmaction/apis/test.py +204 -0
  19. TransRAC/mmaction/apis/train.py +261 -0
  20. TransRAC/mmaction/core/__init__.py +6 -0
  21. TransRAC/mmaction/datasets/__init__.py +27 -0
  22. TransRAC/mmaction/datasets/activitynet_dataset.py +269 -0
  23. TransRAC/mmaction/datasets/audio_dataset.py +69 -0
  24. TransRAC/mmaction/datasets/audio_feature_dataset.py +70 -0
  25. TransRAC/mmaction/datasets/audio_visual_dataset.py +76 -0
  26. TransRAC/mmaction/datasets/ava_dataset.py +382 -0
  27. TransRAC/mmaction/datasets/base.py +287 -0
  28. TransRAC/mmaction/datasets/builder.py +132 -0
  29. TransRAC/mmaction/datasets/dataset_wrappers.py +30 -0
  30. TransRAC/mmaction/datasets/hvu_dataset.py +191 -0
  31. TransRAC/mmaction/datasets/image_dataset.py +45 -0
  32. TransRAC/mmaction/datasets/pose_dataset.py +98 -0
  33. TransRAC/mmaction/datasets/rawframe_dataset.py +183 -0
  34. TransRAC/mmaction/datasets/rawvideo_dataset.py +146 -0
  35. TransRAC/mmaction/datasets/ssn_dataset.py +881 -0
  36. TransRAC/mmaction/datasets/video_dataset.py +60 -0
  37. TransRAC/mmaction/localization/__init__.py +10 -0
  38. TransRAC/mmaction/localization/bsn_utils.py +267 -0
  39. TransRAC/mmaction/localization/proposal_utils.py +94 -0
  40. TransRAC/mmaction/localization/ssn_utils.py +168 -0
  41. TransRAC/mmaction/utils/decorators.py +32 -0
  42. TransRAC/mmaction/utils/logger.py +24 -0
  43. TransRAC/mmaction/utils/optimizer.py +33 -0
  44. TransRAC/mmaction/version.py +18 -0
  45. TransRAC/mmcv_custom/__init__.py +1 -0
  46. TransRAC/models/TransRAC.py +254 -0
  47. TransRAC/models/__init__.py +0 -0
  48. TransRAC/open_set/new_test.csv +0 -0
  49. TransRAC/open_set/new_train.csv +0 -0
  50. TransRAC/open_set/new_valid.csv +129 -0
TransRAC/.idea/.gitignore ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
4
+ # Datasource local storage ignored files
5
+ /dataSources/
6
+ /dataSources.local.xml
7
+ # Editor-based HTTP Client requests
8
+ /httpRequests/
TransRAC/.idea/SVIP_Counting.iml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$" />
5
+ <orderEntry type="inheritedJdk" />
6
+ <orderEntry type="sourceFolder" forTests="false" />
7
+ </component>
8
+ </module>
TransRAC/.idea/deployment.xml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
4
+ <serverData>
5
+ <paths name="root@10.15.89.41:23035 password">
6
+ <serverdata>
7
+ <mappings>
8
+ <mapping local="$PROJECT_DIR$" web="/" />
9
+ </mappings>
10
+ </serverdata>
11
+ </paths>
12
+ </serverData>
13
+ </component>
14
+ </project>
TransRAC/.idea/misc.xml ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8" project-jdk-type="Python SDK" />
4
+ </project>
TransRAC/.idea/modules.xml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/SVIP_Counting.iml" filepath="$PROJECT_DIR$/.idea/SVIP_Counting.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
TransRAC/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
TransRAC/dataset/RepCountA_Loader.py ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ '''
2
+ Repcount data loader from fixed frames file(.npz) which will be uploaded soon.
3
+ if you don't pre-process the data file,for example,your raw file is .mp4,
4
+ you can use the *RepCountA_raw_Loader.py*(slowly).
5
+ or
6
+ you can use 'tools.video2npz.py' to transform .mp4 tp .npz
7
+ '''
8
+ import csv
9
+ import os
10
+ import os.path as osp
11
+ import numpy as np
12
+ import math
13
+
14
+ from torch.utils.data import Dataset, DataLoader
15
+ import torch
16
+ from .label_norm import normalize_label
17
+
18
+
19
+ class MyData(Dataset):
20
+
21
+ def __init__(self, root_path, video_path, label_path, num_frame):
22
+ """
23
+ :param root_path: root path
24
+ :param video_path: video child path (folder)
25
+ :param label_path: label child path(.csv)
26
+ """
27
+ self.root_path = root_path
28
+ self.video_path = os.path.join(self.root_path, video_path) # train or valid
29
+ self.label_path = os.path.join(self.root_path, label_path)
30
+ self.video_dir = os.listdir(self.video_path)
31
+ self.label_dict = get_labels_dict(self.label_path) # get all labels
32
+ self.num_frame = num_frame
33
+
34
+ def __getitem__(self, inx):
35
+ """ get data item
36
+ :param video_tensor, label
37
+ """
38
+ video_file_name = self.video_dir[inx]
39
+ file_path = os.path.join(self.video_path, video_file_name)
40
+ video_tensor, video_frame_length = get_frames(file_path) # [64, 3, 224, 224]
41
+ video_tensor = video_tensor.transpose(0, 1) # [64, 3, 224, 224] -> [ 3, 64, 224, 224]
42
+ if video_file_name in self.label_dict.keys():
43
+ time_points = self.label_dict[video_file_name]
44
+ label = preprocess(video_frame_length, time_points, num_frames=self.num_frame)
45
+ label = torch.tensor(label)
46
+ return [video_tensor, label]
47
+ else:
48
+ print(video_file_name, 'not exist')
49
+ return
50
+
51
+ def __len__(self):
52
+ """:return the number of video """
53
+ return len(self.video_dir)
54
+
55
+
56
+ def get_frames(npz_path):
57
+ # get frames from .npz files
58
+ with np.load(npz_path, allow_pickle=True) as data:
59
+ frames = data['imgs'] # numpy.narray [64, 3, 224, 224]
60
+ frames_length = data['fps'].item() # the raw video(.mp4) total frames number
61
+ frames = torch.FloatTensor(frames)
62
+ frames -= 127.5
63
+ frames /= 127.5
64
+ return frames, frames_length
65
+
66
+
67
+ def get_labels_dict(path):
68
+ # read label.csv to RAM
69
+ labels_dict = {}
70
+ check_file_exist(path)
71
+ with open(path, encoding='utf-8') as f:
72
+ f_csv = csv.DictReader(f)
73
+ for row in f_csv:
74
+ cycle = [int(float(row[key])) for key in row.keys() if 'L' in key and row[key] != '']
75
+ if not row['count']:
76
+ print(row['name'] + 'error')
77
+ else:
78
+ labels_dict[row['name'].split('.')[0] + str('.npz')] = cycle
79
+
80
+ return labels_dict
81
+
82
+
83
+ def preprocess(video_frame_length, time_points, num_frames):
84
+ """
85
+ process label(.csv) to density map label
86
+ Args:
87
+ video_frame_length: video total frame number, i.e 1024frames
88
+ time_points: label point example [1, 23, 23, 40,45,70,.....] or [0]
89
+ num_frames: 64
90
+ Returns: for example [0.1,0.8,0.1, .....]
91
+ """
92
+ new_crop = []
93
+ for i in range(len(time_points)): # frame_length -> 64
94
+ item = min(math.ceil((float((time_points[i])) / float(video_frame_length)) * num_frames), num_frames - 1)
95
+ new_crop.append(item)
96
+ new_crop = np.sort(new_crop)
97
+ label = normalize_label(new_crop, num_frames)
98
+
99
+ return label
100
+
101
+
102
+ def check_file_exist(filename, msg_tmpl='file "{}" does not exist'):
103
+ if not osp.isfile(filename):
104
+ raise FileNotFoundError(msg_tmpl.format(filename))
TransRAC/dataset/RepCountA_raw_Loader.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ If you don't want to pre-process the RepCount dataset,
3
+ you can use this script to load data from raw video(.mp4).
4
+ The trainning speed is slower and the memory cost more.
5
+ """
6
+
7
+ import os
8
+ import os.path as osp
9
+ import numpy as np
10
+ import math
11
+ import cv2
12
+ from torch.utils.data import Dataset
13
+ import torch
14
+ import csv
15
+ import kornia
16
+ from .label_norm import normalize_label
17
+ import torchvision.transforms as transforms
18
+
19
+ class MyData(Dataset):
20
+
21
+ def __init__(self, root_path, video_path, label_path, num_frame):
22
+ self.root_path = root_path
23
+ self.video_path = video_path
24
+ self.label_dir = os.path.join(root_path, label_path)
25
+ self.video_dir = os.listdir(os.path.join(self.root_path, self.video_path))
26
+ self.label_dict = get_labels_dict(self.label_dir) # get all labels
27
+ self.num_frame = num_frame
28
+
29
+ def __getitem__(self, inx):
30
+ video_file_name= self.video_dir[inx]
31
+ file_path = os.path.join(self.root_path, self.video_path, video_file_name)
32
+ video_rd = VideoRead(file_path, num_frames=self.num_frame)
33
+ video_tensor = video_rd.crop_frame()
34
+ video_frame_length = video_rd.frame_length
35
+ video_tensor = video_tensor.transpose(0, 1) # [64, 3, 224, 224] -> [ 3, 64, 224, 224]
36
+ if video_file_name in self.label_dict.keys():
37
+ time_points = self.label_dict[video_file_name]
38
+ label = preprocess(video_frame_length, time_points, num_frames=self.num_frame)
39
+ label = torch.tensor(label)
40
+ return [video_tensor, label]
41
+ else:
42
+ print(video_file_name, 'not exist')
43
+ return
44
+
45
+ def __len__(self):
46
+ """返回数据集的大小"""
47
+ return len(self.video_dir)
48
+
49
+
50
+ class VideoRead:
51
+ def __init__(self, video_path, num_frames):
52
+ self.video_path = video_path
53
+ self.frame_length = 0
54
+ self.num_frames = num_frames
55
+
56
+ def get_frame(self):
57
+ cap = cv2.VideoCapture(self.video_path)
58
+ assert cap.isOpened()
59
+ frames = []
60
+
61
+ while cap.isOpened():
62
+ ret, frame = cap.read()
63
+ if not ret:
64
+ break
65
+ frames.append(frame)
66
+ cap.release()
67
+ self.frame_length = len(frames)
68
+ return frames
69
+
70
+ def crop_frame(self):
71
+ """to crop frames to tensor
72
+ return: tensor [64, 3, 224, 224]
73
+ """
74
+ frames = self.get_frame() # frames: the all frames of video
75
+ frames_tensor = []
76
+ if self.num_frames <= len(frames):
77
+ for i in range(self.num_frames):
78
+ # select 64 frames from total original frames, proportionally
79
+ frame = frames[i * len(frames) // self.num_frames]
80
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
81
+ frame = cv2.resize(frame, (224, 224)) # [3, 224, 224]
82
+ frame = transforms.ToTensor()(frame)
83
+ frames_tensor.append(frame)
84
+
85
+ else: # if raw frames number lower than 64, padding it.
86
+ for i in range(self.frame_length):
87
+ frame = frames[i]
88
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
89
+ frame = cv2.resize(frame, (224, 224)) # [ 3, 224, 224]
90
+ frame = transforms.ToTensor()(frame)
91
+ frames_tensor.append(frame)
92
+ for i in range(self.num_frames - self.frame_length):
93
+ frame = frames[self.frame_length - 1]
94
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
95
+ frame = cv2.resize(frame, (224, 224)) # [ 3, 224, 224]
96
+ frame = transforms.ToTensor()(frame)
97
+ frames_tensor.append(frame)
98
+ Frame_Tensor=torch.as_tensor(np.stack(frames_tensor))
99
+
100
+ return Frame_Tensor
101
+
102
+
103
+ def get_labels_dict(path):
104
+ labels_dict = {}
105
+ check_file_exist(path)
106
+ with open(path, encoding='utf-8') as f:
107
+ f_csv = csv.DictReader(f)
108
+ for row in f_csv:
109
+ cycle = [int(row[key]) for key in row.keys() if 'L' in key and row[key] != '']
110
+ labels_dict[row['name']] = cycle
111
+
112
+ return labels_dict
113
+
114
+ def preprocess(video_frame_length, time_points, num_frames):
115
+ """
116
+ process label(.csv) to density map label
117
+ Args:
118
+ video_frame_length: video total frame number, i.e 1024frames
119
+ time_points: label point example [1, 23, 23, 40,45,70,.....] or [0]
120
+ num_frames: 64
121
+ Returns: for example list [0.1,0.8,0.1, .....]
122
+ """
123
+ new_crop = []
124
+ for i in range(len(time_points)): # frame_length -> 64
125
+ item = min(math.ceil((float((time_points[i])) / float(video_frame_length)) * num_frames), num_frames - 1)
126
+ new_crop.append(item)
127
+ new_crop = np.sort(new_crop)
128
+ label = normalize_label(new_crop, num_frames)
129
+
130
+ return label
131
+
132
+ def check_file_exist(filename, msg_tmpl='file "{}" does not exist'):
133
+ if not osp.isfile(filename):
134
+ raise FileNotFoundError(msg_tmpl.format(filename))
135
+
136
+ # # example
137
+ # root_dir = r'/p300/data/dataset/'
138
+ # video_dir = 'train'
139
+ # label_dir = 'train.csv'
140
+ # train_dataset = MyData(root_dir, video_dir, label_dir,64)
141
+ # trainloader = DataLoader(train_dataset, batch_size=1, shuffle=True)
142
+
TransRAC/dataset/RepCountB_Loader.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import numpy as np
4
+ import csv
5
+ import cv2
6
+ from torch.utils.data import Dataset, DataLoader
7
+ import kornia
8
+ import torch
9
+ import torchvision.transforms as transforms
10
+
11
+
12
+ class TestData(Dataset):
13
+ def __init__(self, root_path, video_path, label_path, num_frame):
14
+ self.root_path = root_path
15
+ self.video_path = video_path
16
+ self.label_dir = os.path.join(root_path, label_path)
17
+ self.video_dir = os.listdir(os.path.join(self.root_path, self.video_path))
18
+ self.label_dict = get_labels_dict(self.label_dir) # get all labels
19
+ self.num_frame = num_frame
20
+
21
+ def __getitem__(self, inx):
22
+ video_name= self.video_dir[inx]
23
+ file_path = os.path.join(self.root_path, self.video_path, video_name)
24
+ video_rd = VideoRead(file_path, num_frames=self.num_frame)
25
+ video_tensor = video_rd.crop_frame()
26
+ video_frame_length = video_rd.frame_length
27
+ video_tensor = video_tensor.transpose(0, 1) # [64, 3, 224, 224] -> [ 3, 64, 224, 224]
28
+ count = self.label_dict[video_name]
29
+ count = torch.tensor(count)
30
+
31
+ return video_tensor, count
32
+ def __len__(self):
33
+ """返回数据集的大小"""
34
+ return len(self.video_dir)
35
+
36
+
37
+ class VideoRead:
38
+ def __init__(self, video_path, num_frames=64):
39
+ self.video_path = video_path
40
+ self.frame_length = 0
41
+ self.num_frames = num_frames
42
+
43
+ def get_frame(self):
44
+ cap = cv2.VideoCapture(self.video_path)
45
+ assert cap.isOpened()
46
+ frames = []
47
+
48
+ while cap.isOpened():
49
+ ret, frame = cap.read()
50
+ if not ret:
51
+ break
52
+ frames.append(frame)
53
+ cap.release()
54
+ self.frame_length = len(frames)
55
+ return frames
56
+
57
+ def crop_frame(self):
58
+ """to crop frames to tensor [64, 3, 224, 224]"""
59
+ frames = self.get_frame() # frames: the all frames of video
60
+ frames_tensor = []
61
+ if self.num_frames <= len(frames):
62
+ for i in range(self.num_frames):
63
+ # select 64 frames from total original frames, proportionally
64
+ frame = frames[i * len(frames) // self.num_frames]
65
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
66
+ frame = cv2.resize(frame, (224, 224)) # [64, 3, 224, 224]
67
+ frame = transforms.ToTensor()(frame)
68
+ frames_tensor.append(frame)
69
+
70
+ else: # if raw frames number lower than 64, padding it.
71
+ for i in range(self.frame_length):
72
+ frame = frames[i]
73
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
74
+ frame = cv2.resize(frame, (224, 224)) # [64, 3, 224, 224]
75
+ frame = transforms.ToTensor()(frame)
76
+ frames_tensor.append(frame)
77
+ for i in range(self.num_frames - self.frame_length):
78
+ frame = frames[self.frame_length - 1]
79
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
80
+ frame = cv2.resize(frame, (224, 224)) # [64, 3, 224, 224]
81
+ frame = transforms.ToTensor()(frame)
82
+ frames_tensor.append(frame)
83
+ # frames_tensor = np.asarray_chkfinite(frames_tensor, dtype=np.uint8)
84
+ # frames_tensor = kornia.image_to_tensor(frames_tensor, keepdim=False).div(255.0)
85
+ Frame_Tensor=torch.as_tensor(np.stack(frames_tensor))
86
+
87
+ return Frame_Tensor
88
+
89
+
90
+ def get_labels_dict(path):
91
+ labels_dict = {}
92
+ check_file_exist(path)
93
+ with open(path, encoding='utf-8') as f:
94
+ f_csv = csv.DictReader(f)
95
+ for row in f_csv:
96
+ cycle = [int(row[key]) for key in row.keys() if 'L' in key and row[key] != '']
97
+ if not row['count']:
98
+ print(row['name']+'error')
99
+ else:
100
+ labels_dict[row['name']] = int(row['count'])
101
+
102
+ return labels_dict
103
+
104
+
105
+ def check_file_exist(filename, msg_tmpl='file "{}" does not exist'):
106
+ if not os.path.isfile(filename):
107
+ raise FileNotFoundError(msg_tmpl.format(filename))
108
+
109
+
110
+
111
+
TransRAC/dataset/UCFRep_loader.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import numpy as np
4
+ import csv
5
+ import cv2
6
+ from torch.utils.data import Dataset, DataLoader
7
+ import kornia
8
+ import torch
9
+
10
+ class TestData(Dataset):
11
+ def __init__(self, root_path, video_path, label_path, num_frame):
12
+ """
13
+ Args:
14
+ root_path:
15
+ video_path:
16
+ label_path:
17
+ num_frame:
18
+ """
19
+ self.root_path = root_path
20
+ self.video_path = video_path
21
+ self.label_dir = os.path.join(root_path, label_path)
22
+ self.video_dir = os.listdir(os.path.join(self.root_path, self.video_path))
23
+ self.label_dict = get_labels_dict(self.label_dir) # get all labels
24
+ self.num_frame = num_frame
25
+
26
+ def __getitem__(self, inx):
27
+ """获取数据集中的item """
28
+
29
+ video_name= self.video_dir[inx]
30
+ file_path = os.path.join(self.root_path, self.video_path, video_name)
31
+ video_rd = VideoRead(file_path, num_frames=self.num_frame)
32
+ video_tensor = video_rd.crop_frame()
33
+ video_frame_length = video_rd.frame_length
34
+ video_tensor = video_tensor.transpose(0, 1) # [64, 3, 224, 224] -> [ 3, 64, 224, 224]
35
+ count = self.label_dict[video_name]
36
+ count = torch.tensor(count)
37
+
38
+ return video_tensor, count
39
+ def __len__(self):
40
+ """返回数据集的大小"""
41
+ return len(self.video_dir)
42
+
43
+
44
+ class VideoRead:
45
+ def __init__(self, video_path, num_frames):
46
+ self.video_path = video_path
47
+ self.frame_length = 0
48
+ self.num_frames = num_frames
49
+
50
+ def get_frame(self):
51
+ cap = cv2.VideoCapture(self.video_path)
52
+ assert cap.isOpened()
53
+ frames = []
54
+
55
+ while cap.isOpened():
56
+ ret, frame = cap.read()
57
+ if not ret:
58
+ break
59
+ frames.append(frame)
60
+ cap.release()
61
+ self.frame_length = len(frames)
62
+ return frames
63
+
64
+ def crop_frame(self):
65
+ """to crop frames to 64 frames"""
66
+ frames = self.get_frame()
67
+ frames_tensor = []
68
+ if self.num_frames <= self.frame_length:
69
+ for i in range(1, self.num_frames + 1):
70
+ frame = frames[i * len(frames) // self.num_frames - 1] # Proportional extraction (64 frames)
71
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
72
+ frame = cv2.resize(frame, (224, 224)) # [64, 3, 224, 224]
73
+ # frame = transform(frame).unsqueeze(0)
74
+ frames_tensor.append(frame)
75
+
76
+ else: # if raw frames number lower than 64, padding it.
77
+ for i in range(self.frame_length):
78
+ frame = frames[i]
79
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
80
+ frame = cv2.resize(frame, (224, 224)) # [64, 3, 224, 224]
81
+ frames_tensor.append(frame)
82
+ for i in range(self.num_frames - self.frame_length):
83
+ frame = frames[self.frame_length - 1]
84
+ frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
85
+ frame = cv2.resize(frame, (224, 224)) # [64, 3, 224, 224]
86
+ frames_tensor.append(frame)
87
+ frames_tensor = np.asarray_chkfinite(frames_tensor, dtype=np.uint8)
88
+ frames_tensor = kornia.image_to_tensor(frames_tensor, keepdim=False).div(255.0)
89
+
90
+ return frames_tensor
91
+
92
+
93
+ def get_labels_dict(path):
94
+ labels_dict = {}
95
+ check_file_exist(path)
96
+ with open(path, encoding='utf-8') as f:
97
+ f_csv = csv.DictReader(f)
98
+ for row in f_csv:
99
+ if not row['count']:
100
+ print(row['name']+'error')
101
+ else:
102
+ labels_dict[row['name']] = int(row['count'])
103
+
104
+ return labels_dict
105
+
106
+
107
+ def check_file_exist(filename, msg_tmpl='file "{}" does not exist'):
108
+ if not os.path.isfile(filename):
109
+ raise FileNotFoundError(msg_tmpl.format(filename))
110
+
111
+
112
+
113
+
TransRAC/dataset/__init__.py ADDED
File without changes
TransRAC/dataset/label_norm.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ transform label to groundtruth(density map)"""
2
+ from scipy import integrate
3
+ import math
4
+ import numpy as np
5
+
6
+
7
+ def PDF(x, u, sig):
8
+ # f(x)
9
+ return np.exp(-(x - u) ** 2 / (2 * sig ** 2)) / (math.sqrt(2 * math.pi) * sig)
10
+
11
+ # integral f(x)
12
+ def get_integrate(x_1, x_2, avg, sig):
13
+ y, err = integrate.quad(PDF, x_1, x_2, args=(avg, sig))
14
+ return y
15
+
16
+
17
+ def normalize_label(y_frame, y_length):
18
+ # y_length: total frames
19
+ # return: normalize_label size:nparray(y_length,)
20
+ y_label = [0 for i in range(y_length)] # 坐标轴长度,即帧数
21
+ for i in range(0, len(y_frame), 2):
22
+ x_a = y_frame[i]
23
+ x_b = y_frame[i + 1]
24
+ avg = (x_b + x_a) / 2
25
+ sig = (x_b - x_a) / 6
26
+ num = x_b - x_a + 1 # 帧数量 update 1104
27
+ if num != 1:
28
+ for j in range(num):
29
+ x_1 = x_a - 0.5 + j
30
+ x_2 = x_a + 0.5 + j
31
+ y_ing = get_integrate(x_1, x_2, avg, sig)
32
+ y_label[x_a + j] = y_ing
33
+ else:
34
+ y_label[x_a] = 1
35
+ return y_label
TransRAC/figures/readme.md ADDED
@@ -0,0 +1 @@
 
 
1
+
TransRAC/log/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ ## store log
TransRAC/mmaction/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import mmcv
2
+ from mmcv import digit_version
3
+
4
+ from .version import __version__
5
+
6
+ mmcv_minimum_version = '1.3.1'
7
+ mmcv_maximum_version = '1.4.0'
8
+ mmcv_version = digit_version(mmcv.__version__)
9
+
10
+ assert (digit_version(mmcv_minimum_version) <= mmcv_version
11
+ <= digit_version(mmcv_maximum_version)), \
12
+ f'MMCV=={mmcv.__version__} is used but incompatible. ' \
13
+ f'Please install mmcv>={mmcv_minimum_version}, <={mmcv_maximum_version}.'
14
+
15
+ __all__ = ['__version__']
TransRAC/mmaction/apis/__init__.py ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ from .inference import inference_recognizer, init_recognizer
2
+ from .test import multi_gpu_test, single_gpu_test
3
+ from .train import train_model
4
+
5
+ __all__ = [
6
+ 'train_model', 'init_recognizer', 'inference_recognizer', 'multi_gpu_test',
7
+ 'single_gpu_test'
8
+ ]
TransRAC/mmaction/apis/inference.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import os.path as osp
3
+ import re
4
+ from operator import itemgetter
5
+
6
+ import mmcv
7
+ import torch
8
+ from mmcv.parallel import collate, scatter
9
+ from mmcv.runner import load_checkpoint
10
+
11
+ from mmaction.core import OutputHook
12
+ from mmaction.datasets.pipelines import Compose
13
+ from mmaction.models import build_recognizer
14
+
15
+
16
+ def init_recognizer(config,
17
+ checkpoint=None,
18
+ device='cuda:0',
19
+ use_frames=False):
20
+ """Initialize a recognizer from config file.
21
+
22
+ Args:
23
+ config (str | :obj:`mmcv.Config`): Config file path or the config
24
+ object.
25
+ checkpoint (str | None, optional): Checkpoint path/url. If set to None,
26
+ the model will not load any weights. Default: None.
27
+ device (str | :obj:`torch.device`): The desired device of returned
28
+ tensor. Default: 'cuda:0'.
29
+ use_frames (bool): Whether to use rawframes as input. Default:False.
30
+
31
+ Returns:
32
+ nn.Module: The constructed recognizer.
33
+ """
34
+ if isinstance(config, str):
35
+ config = mmcv.Config.fromfile(config)
36
+ elif not isinstance(config, mmcv.Config):
37
+ raise TypeError('config must be a filename or Config object, '
38
+ f'but got {type(config)}')
39
+ if ((use_frames and config.dataset_type != 'RawframeDataset')
40
+ or (not use_frames and config.dataset_type != 'VideoDataset')):
41
+ input_type = 'rawframes' if use_frames else 'video'
42
+ raise RuntimeError('input data type should be consist with the '
43
+ f'dataset type in config, but got input type '
44
+ f"'{input_type}' and dataset type "
45
+ f"'{config.dataset_type}'")
46
+
47
+ # pretrained model is unnecessary since we directly load checkpoint later
48
+ config.model.backbone.pretrained = None
49
+ model = build_recognizer(config.model, test_cfg=config.get('test_cfg'))
50
+
51
+ if checkpoint is not None:
52
+ load_checkpoint(model, checkpoint, map_location=device)
53
+ model.cfg = config
54
+ model.to(device)
55
+ model.eval()
56
+ return model
57
+
58
+
59
+ def inference_recognizer(model,
60
+ video_path,
61
+ label_path,
62
+ use_frames=False,
63
+ outputs=None,
64
+ as_tensor=True):
65
+ """Inference a video with the detector.
66
+
67
+ Args:
68
+ model (nn.Module): The loaded recognizer.
69
+ video_path (str): The video file path/url or the rawframes directory
70
+ path. If ``use_frames`` is set to True, it should be rawframes
71
+ directory path. Otherwise, it should be video file path.
72
+ label_path (str): The label file path.
73
+ use_frames (bool): Whether to use rawframes as input. Default:False.
74
+ outputs (list(str) | tuple(str) | str | None) : Names of layers whose
75
+ outputs need to be returned, default: None.
76
+ as_tensor (bool): Same as that in ``OutputHook``. Default: True.
77
+
78
+ Returns:
79
+ dict[tuple(str, float)]: Top-5 recognition result dict.
80
+ dict[torch.tensor | np.ndarray]:
81
+ Output feature maps from layers specified in `outputs`.
82
+ """
83
+ if not (osp.exists(video_path) or video_path.startswith('http')):
84
+ raise RuntimeError(f"'{video_path}' is missing")
85
+
86
+ if osp.isfile(video_path) and use_frames:
87
+ raise RuntimeError(
88
+ f"'{video_path}' is a video file, not a rawframe directory")
89
+ if osp.isdir(video_path) and not use_frames:
90
+ raise RuntimeError(
91
+ f"'{video_path}' is a rawframe directory, not a video file")
92
+
93
+ if isinstance(outputs, str):
94
+ outputs = (outputs, )
95
+ assert outputs is None or isinstance(outputs, (tuple, list))
96
+
97
+ cfg = model.cfg
98
+ device = next(model.parameters()).device # model device
99
+ # construct label map
100
+ with open(label_path, 'r') as f:
101
+ label = [line.strip() for line in f]
102
+ # build the data pipeline
103
+ test_pipeline = cfg.data.test.pipeline
104
+ test_pipeline = Compose(test_pipeline)
105
+ # prepare data
106
+ if use_frames:
107
+ filename_tmpl = cfg.data.test.get('filename_tmpl', 'img_{:05}.jpg')
108
+ modality = cfg.data.test.get('modality', 'RGB')
109
+ start_index = cfg.data.test.get('start_index', 1)
110
+
111
+ # count the number of frames that match the format of `filename_tmpl`
112
+ # RGB pattern example: img_{:05}.jpg -> ^img_\d+.jpg$
113
+ # Flow patteren example: {}_{:05d}.jpg -> ^x_\d+.jpg$
114
+ pattern = f'^{filename_tmpl}$'
115
+ if modality == 'Flow':
116
+ pattern = pattern.replace('{}', 'x')
117
+ pattern = pattern.replace(
118
+ pattern[pattern.find('{'):pattern.find('}') + 1], '\\d+')
119
+ total_frames = len(
120
+ list(
121
+ filter(lambda x: re.match(pattern, x) is not None,
122
+ os.listdir(video_path))))
123
+
124
+ data = dict(
125
+ frame_dir=video_path,
126
+ total_frames=total_frames,
127
+ label=-1,
128
+ start_index=start_index,
129
+ filename_tmpl=filename_tmpl,
130
+ modality=modality)
131
+ else:
132
+ start_index = cfg.data.test.get('start_index', 0)
133
+ data = dict(
134
+ filename=video_path,
135
+ label=-1,
136
+ start_index=start_index,
137
+ modality='RGB')
138
+ data = test_pipeline(data)
139
+ data = collate([data], samples_per_gpu=1)
140
+ if next(model.parameters()).is_cuda:
141
+ # scatter to specified GPU
142
+ data = scatter(data, [device])[0]
143
+
144
+ # forward the model
145
+ with OutputHook(model, outputs=outputs, as_tensor=as_tensor) as h:
146
+ with torch.no_grad():
147
+ scores = model(return_loss=False, **data)[0]
148
+ returned_features = h.layer_outputs if outputs else None
149
+
150
+ score_tuples = tuple(zip(label, scores))
151
+ score_sorted = sorted(score_tuples, key=itemgetter(1), reverse=True)
152
+
153
+ top5_label = score_sorted[:5]
154
+ if outputs:
155
+ return top5_label, returned_features
156
+ return top5_label
TransRAC/mmaction/apis/test.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+ import pickle
3
+ import shutil
4
+ import tempfile
5
+ # TODO import test functions from mmcv and delete them from mmaction2
6
+ import warnings
7
+
8
+ import mmcv
9
+ import torch
10
+ import torch.distributed as dist
11
+ from mmcv.runner import get_dist_info
12
+
13
+ try:
14
+ from mmcv.engine import (single_gpu_test, multi_gpu_test,
15
+ collect_results_gpu, collect_results_cpu)
16
+ from_mmcv = True
17
+ except (ImportError, ModuleNotFoundError):
18
+ warnings.warn(
19
+ 'DeprecationWarning: single_gpu_test, multi_gpu_test, '
20
+ 'collect_results_cpu, collect_results_gpu from mmaction2 will be '
21
+ 'deprecated. Please install mmcv through master branch.')
22
+ from_mmcv = False
23
+
24
+ if not from_mmcv:
25
+
26
+ def single_gpu_test(model, data_loader): # noqa: F811
27
+ """Test model with a single gpu.
28
+
29
+ This method tests model with a single gpu and
30
+ displays test progress bar.
31
+
32
+ Args:
33
+ model (nn.Module): Model to be tested.
34
+ data_loader (nn.Dataloader): Pytorch data loader.
35
+
36
+ Returns:
37
+ list: The prediction results.
38
+ """
39
+ model.eval()
40
+ results = []
41
+ dataset = data_loader.dataset
42
+ prog_bar = mmcv.ProgressBar(len(dataset))
43
+ for data in data_loader:
44
+ with torch.no_grad():
45
+ result = model(return_loss=False, **data)
46
+ results.extend(result)
47
+
48
+ # use the first key as main key to calculate the batch size
49
+ batch_size = len(next(iter(data.values())))
50
+ for _ in range(batch_size):
51
+ prog_bar.update()
52
+ return results
53
+
54
+ def multi_gpu_test( # noqa: F811
55
+ model, data_loader, tmpdir=None, gpu_collect=True):
56
+ """Test model with multiple gpus.
57
+
58
+ This method tests model with multiple gpus and collects the results
59
+ under two different modes: gpu and cpu modes. By setting
60
+ 'gpu_collect=True' it encodes results to gpu tensors and use gpu
61
+ communication for results collection. On cpu mode it saves the results
62
+ on different gpus to 'tmpdir' and collects them by the rank 0 worker.
63
+
64
+ Args:
65
+ model (nn.Module): Model to be tested.
66
+ data_loader (nn.Dataloader): Pytorch data loader.
67
+ tmpdir (str): Path of directory to save the temporary results from
68
+ different gpus under cpu mode. Default: None
69
+ gpu_collect (bool): Option to use either gpu or cpu to collect
70
+ results. Default: True
71
+
72
+ Returns:
73
+ list: The prediction results.
74
+ """
75
+ model.eval()
76
+ results = []
77
+ dataset = data_loader.dataset
78
+ rank, world_size = get_dist_info()
79
+ if rank == 0:
80
+ prog_bar = mmcv.ProgressBar(len(dataset))
81
+ for data in data_loader:
82
+ with torch.no_grad():
83
+ result = model(return_loss=False, **data)
84
+ results.extend(result)
85
+
86
+ if rank == 0:
87
+ # use the first key as main key to calculate the batch size
88
+ batch_size = len(next(iter(data.values())))
89
+ for _ in range(batch_size * world_size):
90
+ prog_bar.update()
91
+
92
+ # collect results from all ranks
93
+ if gpu_collect:
94
+ results = collect_results_gpu(results, len(dataset))
95
+ else:
96
+ results = collect_results_cpu(results, len(dataset), tmpdir)
97
+ return results
98
+
99
+ def collect_results_cpu(result_part, size, tmpdir=None): # noqa: F811
100
+ """Collect results in cpu mode.
101
+
102
+ It saves the results on different gpus to 'tmpdir' and collects
103
+ them by the rank 0 worker.
104
+
105
+ Args:
106
+ result_part (list): Results to be collected
107
+ size (int): Result size.
108
+ tmpdir (str): Path of directory to save the temporary results from
109
+ different gpus under cpu mode. Default: None
110
+
111
+ Returns:
112
+ list: Ordered results.
113
+ """
114
+ rank, world_size = get_dist_info()
115
+ # create a tmp dir if it is not specified
116
+ if tmpdir is None:
117
+ MAX_LEN = 512
118
+ # 32 is whitespace
119
+ dir_tensor = torch.full((MAX_LEN, ),
120
+ 32,
121
+ dtype=torch.uint8,
122
+ device='cuda')
123
+ if rank == 0:
124
+ mmcv.mkdir_or_exist('.dist_test')
125
+ tmpdir = tempfile.mkdtemp(dir='.dist_test')
126
+ tmpdir = torch.tensor(
127
+ bytearray(tmpdir.encode()),
128
+ dtype=torch.uint8,
129
+ device='cuda')
130
+ dir_tensor[:len(tmpdir)] = tmpdir
131
+ dist.broadcast(dir_tensor, 0)
132
+ tmpdir = dir_tensor.cpu().numpy().tobytes().decode().rstrip()
133
+ else:
134
+ mmcv.mkdir_or_exist(tmpdir)
135
+ # synchronizes all processes to make sure tmpdir exist
136
+ dist.barrier()
137
+ # dump the part result to the dir
138
+ mmcv.dump(result_part, osp.join(tmpdir, f'part_{rank}.pkl'))
139
+ # synchronizes all processes for loding pickle file
140
+ dist.barrier()
141
+ # collect all parts
142
+ if rank != 0:
143
+ return None
144
+ # load results of all parts from tmp dir
145
+ part_list = []
146
+ for i in range(world_size):
147
+ part_file = osp.join(tmpdir, f'part_{i}.pkl')
148
+ part_list.append(mmcv.load(part_file))
149
+ # sort the results
150
+ ordered_results = []
151
+ for res in zip(*part_list):
152
+ ordered_results.extend(list(res))
153
+ # the dataloader may pad some samples
154
+ ordered_results = ordered_results[:size]
155
+ # remove tmp dir
156
+ shutil.rmtree(tmpdir)
157
+ return ordered_results
158
+
159
+ def collect_results_gpu(result_part, size): # noqa: F811
160
+ """Collect results in gpu mode.
161
+
162
+ It encodes results to gpu tensors and use gpu communication for results
163
+ collection.
164
+
165
+ Args:
166
+ result_part (list): Results to be collected
167
+ size (int): Result size.
168
+
169
+ Returns:
170
+ list: Ordered results.
171
+ """
172
+ rank, world_size = get_dist_info()
173
+ # dump result part to tensor with pickle
174
+ part_tensor = torch.tensor(
175
+ bytearray(pickle.dumps(result_part)),
176
+ dtype=torch.uint8,
177
+ device='cuda')
178
+ # gather all result part tensor shape
179
+ shape_tensor = torch.tensor(part_tensor.shape, device='cuda')
180
+ shape_list = [shape_tensor.clone() for _ in range(world_size)]
181
+ dist.all_gather(shape_list, shape_tensor)
182
+ # padding result part tensor to max length
183
+ shape_max = torch.tensor(shape_list).max()
184
+ part_send = torch.zeros(shape_max, dtype=torch.uint8, device='cuda')
185
+ part_send[:shape_tensor[0]] = part_tensor
186
+ part_recv_list = [
187
+ part_tensor.new_zeros(shape_max) for _ in range(world_size)
188
+ ]
189
+ # gather all result part
190
+ dist.all_gather(part_recv_list, part_send)
191
+
192
+ if rank == 0:
193
+ part_list = []
194
+ for recv, shape in zip(part_recv_list, shape_list):
195
+ part_list.append(
196
+ pickle.loads(recv[:shape[0]].cpu().numpy().tobytes()))
197
+ # sort the results
198
+ ordered_results = []
199
+ for res in zip(*part_list):
200
+ ordered_results.extend(list(res))
201
+ # the dataloader may pad some samples
202
+ ordered_results = ordered_results[:size]
203
+ return ordered_results
204
+ return None
TransRAC/mmaction/apis/train.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy as cp
2
+ import os.path as osp
3
+
4
+ import torch
5
+ from mmcv.parallel import MMDataParallel, MMDistributedDataParallel
6
+ from mmcv.runner import (DistSamplerSeedHook, EpochBasedRunner, OptimizerHook,
7
+ build_optimizer, get_dist_info)
8
+ from mmcv.runner.hooks import Fp16OptimizerHook
9
+
10
+ from ..core import (DistEvalHook, EvalHook, OmniSourceDistSamplerSeedHook,
11
+ OmniSourceRunner)
12
+ from ..datasets import build_dataloader, build_dataset
13
+ from ..utils import PreciseBNHook, get_root_logger
14
+ from .test import multi_gpu_test
15
+ from mmcv_custom.runner import EpochBasedRunnerAmp
16
+ import apex
17
+ import os.path as osp
18
+
19
+
20
+ def train_model(model,
21
+ dataset,
22
+ cfg,
23
+ distributed=False,
24
+ validate=False,
25
+ test=dict(test_best=False, test_last=False),
26
+ timestamp=None,
27
+ meta=None):
28
+ """Train model entry function.
29
+
30
+ Args:
31
+ model (nn.Module): The model to be trained.
32
+ dataset (:obj:`Dataset`): Train dataset.
33
+ cfg (dict): The config dict for training.
34
+ distributed (bool): Whether to use distributed training.
35
+ Default: False.
36
+ validate (bool): Whether to do evaluation. Default: False.
37
+ test (dict): The testing option, with two keys: test_last & test_best.
38
+ The value is True or False, indicating whether to test the
39
+ corresponding checkpoint.
40
+ Default: dict(test_best=False, test_last=False).
41
+ timestamp (str | None): Local time for runner. Default: None.
42
+ meta (dict | None): Meta dict to record some important information.
43
+ Default: None
44
+ """
45
+ logger = get_root_logger(log_level=cfg.log_level)
46
+
47
+ # prepare data loaders
48
+ dataset = dataset if isinstance(dataset, (list, tuple)) else [dataset]
49
+
50
+ if 'optimizer_config' not in cfg:
51
+ cfg.optimizer_config={}
52
+ dataloader_setting = dict(
53
+ videos_per_gpu=cfg.data.get('videos_per_gpu', 1) // cfg.optimizer_config.get('update_interval', 1),
54
+ workers_per_gpu=cfg.data.get('workers_per_gpu', 1),
55
+ num_gpus=len(cfg.gpu_ids),
56
+ dist=distributed,
57
+ seed=cfg.seed)
58
+ dataloader_setting = dict(dataloader_setting,
59
+ **cfg.data.get('train_dataloader', {}))
60
+
61
+ if cfg.omnisource:
62
+ # The option can override videos_per_gpu
63
+ train_ratio = cfg.data.get('train_ratio', [1] * len(dataset))
64
+ omni_videos_per_gpu = cfg.data.get('omni_videos_per_gpu', None)
65
+ if omni_videos_per_gpu is None:
66
+ dataloader_settings = [dataloader_setting] * len(dataset)
67
+ else:
68
+ dataloader_settings = []
69
+ for videos_per_gpu in omni_videos_per_gpu:
70
+ this_setting = cp.deepcopy(dataloader_setting)
71
+ this_setting['videos_per_gpu'] = videos_per_gpu
72
+ dataloader_settings.append(this_setting)
73
+ data_loaders = [
74
+ build_dataloader(ds, **setting)
75
+ for ds, setting in zip(dataset, dataloader_settings)
76
+ ]
77
+
78
+ else:
79
+ data_loaders = [
80
+ build_dataloader(ds, **dataloader_setting) for ds in dataset
81
+ ]
82
+
83
+ # build runner
84
+ optimizer = build_optimizer(model, cfg.optimizer)
85
+ # use apex fp16 optimizer
86
+ # Noticed that this is just a temporary patch. We shoud not encourage this kind of code style
87
+ use_amp = False
88
+ if (
89
+ cfg.optimizer_config.get("type", None)
90
+ and cfg.optimizer_config["type"] == "DistOptimizerHook"
91
+ ):
92
+ if cfg.optimizer_config.get("use_fp16", False):
93
+ model, optimizer = apex.amp.initialize(
94
+ model.cuda(), optimizer, opt_level="O1"
95
+ )
96
+ for m in model.modules():
97
+ if hasattr(m, "fp16_enabled"):
98
+ m.fp16_enabled = True
99
+ use_amp = True
100
+
101
+ # put model on gpus
102
+ if distributed:
103
+ find_unused_parameters = cfg.get('find_unused_parameters', False)
104
+ # Sets the `find_unused_parameters` parameter in
105
+ # torch.nn.parallel.DistributedDataParallel
106
+ model = MMDistributedDataParallel(
107
+ model.cuda(),
108
+ device_ids=[torch.cuda.current_device()],
109
+ broadcast_buffers=False,
110
+ find_unused_parameters=find_unused_parameters)
111
+ else:
112
+ model = MMDataParallel(
113
+ model.cuda(cfg.gpu_ids[0]), device_ids=cfg.gpu_ids)
114
+
115
+ if use_amp:
116
+ Runner = EpochBasedRunnerAmp
117
+ runner = Runner(
118
+ model,
119
+ optimizer=optimizer,
120
+ work_dir=cfg.work_dir,
121
+ logger=logger,
122
+ meta=meta,
123
+ amp=use_amp)
124
+ else:
125
+ Runner = OmniSourceRunner if cfg.omnisource else EpochBasedRunner
126
+ runner = Runner(
127
+ model,
128
+ optimizer=optimizer,
129
+ work_dir=cfg.work_dir,
130
+ logger=logger,
131
+ meta=meta)
132
+ # an ugly workaround to make .log and .log.json filenames the same
133
+ runner.timestamp = timestamp
134
+
135
+ # fp16 setting
136
+ fp16_cfg = cfg.get('fp16', None)
137
+ if fp16_cfg is not None:
138
+ optimizer_config = Fp16OptimizerHook(
139
+ **cfg.optimizer_config, **fp16_cfg, distributed=distributed)
140
+ elif distributed and 'type' not in cfg.optimizer_config:
141
+ optimizer_config = OptimizerHook(**cfg.optimizer_config)
142
+ else:
143
+ optimizer_config = cfg.optimizer_config
144
+
145
+ # register hooks
146
+ runner.register_training_hooks(cfg.lr_config, optimizer_config,
147
+ cfg.checkpoint_config, cfg.log_config,
148
+ cfg.get('momentum_config', None))
149
+ if distributed:
150
+ if cfg.omnisource:
151
+ runner.register_hook(OmniSourceDistSamplerSeedHook())
152
+ else:
153
+ runner.register_hook(DistSamplerSeedHook())
154
+
155
+ # precise bn setting
156
+ if cfg.get('precise_bn', False):
157
+ precise_bn_dataset = build_dataset(cfg.data.train)
158
+ dataloader_setting = dict(
159
+ videos_per_gpu=cfg.data.get('videos_per_gpu', 1),
160
+ workers_per_gpu=0, # save memory and time
161
+ num_gpus=len(cfg.gpu_ids),
162
+ dist=distributed,
163
+ seed=cfg.seed)
164
+ data_loader_precise_bn = build_dataloader(precise_bn_dataset,
165
+ **dataloader_setting)
166
+ precise_bn_hook = PreciseBNHook(data_loader_precise_bn,
167
+ **cfg.get('precise_bn'))
168
+ runner.register_hook(precise_bn_hook)
169
+
170
+ if validate:
171
+ eval_cfg = cfg.get('evaluation', {})
172
+ val_dataset = build_dataset(cfg.data.val, dict(test_mode=True))
173
+ dataloader_setting = dict(
174
+ videos_per_gpu=cfg.data.get('videos_per_gpu', 1),
175
+ workers_per_gpu=cfg.data.get('workers_per_gpu', 1),
176
+ # cfg.gpus will be ignored if distributed
177
+ num_gpus=len(cfg.gpu_ids),
178
+ dist=distributed,
179
+ shuffle=False)
180
+ dataloader_setting = dict(dataloader_setting,
181
+ **cfg.data.get('val_dataloader', {}))
182
+ val_dataloader = build_dataloader(val_dataset, **dataloader_setting)
183
+ eval_hook = DistEvalHook if distributed else EvalHook
184
+ runner.register_hook(eval_hook(val_dataloader, **eval_cfg))
185
+
186
+ if cfg.resume_from:
187
+ runner.resume(cfg.resume_from, resume_amp=use_amp)
188
+ elif cfg.get("auto_resume", False) and osp.exists(osp.join(runner.work_dir, 'latest.pth')):
189
+ runner.auto_resume()
190
+ elif cfg.load_from:
191
+ runner.load_checkpoint(cfg.load_from)
192
+ runner_kwargs = dict()
193
+ if cfg.omnisource:
194
+ runner_kwargs = dict(train_ratio=train_ratio)
195
+ runner.run(data_loaders, cfg.workflow, cfg.total_epochs, **runner_kwargs)
196
+
197
+ if test['test_last'] or test['test_best']:
198
+ best_ckpt_path = None
199
+ if test['test_best']:
200
+ if hasattr(eval_hook, 'best_ckpt_path'):
201
+ best_ckpt_path = eval_hook.best_ckpt_path
202
+
203
+ if best_ckpt_path is None or not osp.exists(best_ckpt_path):
204
+ test['test_best'] = False
205
+ if best_ckpt_path is None:
206
+ runner.logger.info('Warning: test_best set as True, but '
207
+ 'is not applicable '
208
+ '(eval_hook.best_ckpt_path is None)')
209
+ else:
210
+ runner.logger.info('Warning: test_best set as True, but '
211
+ 'is not applicable (best_ckpt '
212
+ f'{best_ckpt_path} not found)')
213
+ if not test['test_last']:
214
+ return
215
+
216
+ test_dataset = build_dataset(cfg.data.test, dict(test_mode=True))
217
+ gpu_collect = cfg.get('evaluation', {}).get('gpu_collect', False)
218
+ tmpdir = cfg.get('evaluation', {}).get('tmpdir',
219
+ osp.join(cfg.work_dir, 'tmp'))
220
+ dataloader_setting = dict(
221
+ videos_per_gpu=cfg.data.get('videos_per_gpu', 1),
222
+ workers_per_gpu=cfg.data.get('workers_per_gpu', 1),
223
+ num_gpus=len(cfg.gpu_ids),
224
+ dist=distributed,
225
+ shuffle=False)
226
+ dataloader_setting = dict(dataloader_setting,
227
+ **cfg.data.get('test_dataloader', {}))
228
+
229
+ test_dataloader = build_dataloader(test_dataset, **dataloader_setting)
230
+
231
+ names, ckpts = [], []
232
+
233
+ if test['test_last']:
234
+ names.append('last')
235
+ ckpts.append(None)
236
+ if test['test_best']:
237
+ names.append('best')
238
+ ckpts.append(best_ckpt_path)
239
+
240
+ for name, ckpt in zip(names, ckpts):
241
+ if ckpt is not None:
242
+ runner.load_checkpoint(ckpt)
243
+
244
+ outputs = multi_gpu_test(runner.model, test_dataloader, tmpdir,
245
+ gpu_collect)
246
+ rank, _ = get_dist_info()
247
+ if rank == 0:
248
+ out = osp.join(cfg.work_dir, f'{name}_pred.pkl')
249
+ test_dataset.dump_results(outputs, out)
250
+
251
+ eval_cfg = cfg.get('evaluation', {})
252
+ for key in [
253
+ 'interval', 'tmpdir', 'start', 'gpu_collect',
254
+ 'save_best', 'rule', 'by_epoch', 'broadcast_bn_buffers'
255
+ ]:
256
+ eval_cfg.pop(key, None)
257
+
258
+ eval_res = test_dataset.evaluate(outputs, **eval_cfg)
259
+ runner.logger.info(f'Testing results of the {name} checkpoint')
260
+ for metric_name, val in eval_res.items():
261
+ runner.logger.info(f'{metric_name}: {val:.04f}')
TransRAC/mmaction/core/__init__.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from .bbox import * # noqa: F401, F403
2
+ from .evaluation import * # noqa: F401, F403
3
+ from .hooks import * # noqa: F401, F403
4
+ from .optimizer import * # noqa: F401, F403
5
+ from .runner import * # noqa: F401, F403
6
+ from .scheduler import * # noqa: F401, F403
TransRAC/mmaction/datasets/__init__.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .activitynet_dataset import ActivityNetDataset
2
+ from .audio_dataset import AudioDataset
3
+ from .audio_feature_dataset import AudioFeatureDataset
4
+ from .audio_visual_dataset import AudioVisualDataset
5
+ from .ava_dataset import AVADataset
6
+ from .base import BaseDataset
7
+ from .blending_utils import (BaseMiniBatchBlending, CutmixBlending,
8
+ MixupBlending, LabelSmoothing)
9
+ from .builder import (BLENDINGS, DATASETS, PIPELINES, build_dataloader,
10
+ build_dataset)
11
+ from .dataset_wrappers import RepeatDataset
12
+ from .hvu_dataset import HVUDataset
13
+ from .image_dataset import ImageDataset
14
+ from .pose_dataset import PoseDataset
15
+ from .rawframe_dataset import RawframeDataset
16
+ from .rawvideo_dataset import RawVideoDataset
17
+ from .ssn_dataset import SSNDataset
18
+ from .video_dataset import VideoDataset
19
+
20
+ __all__ = [
21
+ 'VideoDataset', 'build_dataloader', 'build_dataset', 'RepeatDataset',
22
+ 'RawframeDataset', 'BaseDataset', 'ActivityNetDataset', 'SSNDataset',
23
+ 'HVUDataset', 'AudioDataset', 'AudioFeatureDataset', 'ImageDataset',
24
+ 'RawVideoDataset', 'AVADataset', 'AudioVisualDataset',
25
+ 'BaseMiniBatchBlending', 'CutmixBlending', 'MixupBlending', 'LabelSmoothing', 'DATASETS',
26
+ 'PIPELINES', 'BLENDINGS', 'PoseDataset'
27
+ ]
TransRAC/mmaction/datasets/activitynet_dataset.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os
3
+ import os.path as osp
4
+ import warnings
5
+ from collections import OrderedDict
6
+
7
+ import mmcv
8
+ import numpy as np
9
+
10
+ from ..core import average_recall_at_avg_proposals
11
+ from .base import BaseDataset
12
+ from .builder import DATASETS
13
+
14
+
15
+ @DATASETS.register_module()
16
+ class ActivityNetDataset(BaseDataset):
17
+ """ActivityNet dataset for temporal action localization.
18
+
19
+ The dataset loads raw features and apply specified transforms to return a
20
+ dict containing the frame tensors and other information.
21
+
22
+ The ann_file is a json file with multiple objects, and each object has a
23
+ key of the name of a video, and value of total frames of the video, total
24
+ seconds of the video, annotations of a video, feature frames (frames
25
+ covered by features) of the video, fps and rfps. Example of a
26
+ annotation file:
27
+
28
+ .. code-block:: JSON
29
+
30
+ {
31
+ "v_--1DO2V4K74": {
32
+ "duration_second": 211.53,
33
+ "duration_frame": 6337,
34
+ "annotations": [
35
+ {
36
+ "segment": [
37
+ 30.025882995319815,
38
+ 205.2318595943838
39
+ ],
40
+ "label": "Rock climbing"
41
+ }
42
+ ],
43
+ "feature_frame": 6336,
44
+ "fps": 30.0,
45
+ "rfps": 29.9579255898
46
+ },
47
+ "v_--6bJUbfpnQ": {
48
+ "duration_second": 26.75,
49
+ "duration_frame": 647,
50
+ "annotations": [
51
+ {
52
+ "segment": [
53
+ 2.578755070202808,
54
+ 24.914101404056165
55
+ ],
56
+ "label": "Drinking beer"
57
+ }
58
+ ],
59
+ "feature_frame": 624,
60
+ "fps": 24.0,
61
+ "rfps": 24.1869158879
62
+ },
63
+ ...
64
+ }
65
+
66
+
67
+ Args:
68
+ ann_file (str): Path to the annotation file.
69
+ pipeline (list[dict | callable]): A sequence of data transforms.
70
+ data_prefix (str | None): Path to a directory where videos are held.
71
+ Default: None.
72
+ test_mode (bool): Store True when building test or validation dataset.
73
+ Default: False.
74
+ """
75
+
76
+ def __init__(self, ann_file, pipeline, data_prefix=None, test_mode=False):
77
+ super().__init__(ann_file, pipeline, data_prefix, test_mode)
78
+
79
+ def load_annotations(self):
80
+ """Load the annotation according to ann_file into video_infos."""
81
+ video_infos = []
82
+ anno_database = mmcv.load(self.ann_file)
83
+ for video_name in anno_database:
84
+ video_info = anno_database[video_name]
85
+ video_info['video_name'] = video_name
86
+ video_infos.append(video_info)
87
+ return video_infos
88
+
89
+ def prepare_test_frames(self, idx):
90
+ """Prepare the frames for testing given the index."""
91
+ results = copy.deepcopy(self.video_infos[idx])
92
+ results['data_prefix'] = self.data_prefix
93
+ return self.pipeline(results)
94
+
95
+ def prepare_train_frames(self, idx):
96
+ """Prepare the frames for training given the index."""
97
+ results = copy.deepcopy(self.video_infos[idx])
98
+ results['data_prefix'] = self.data_prefix
99
+ return self.pipeline(results)
100
+
101
+ def __len__(self):
102
+ """Get the size of the dataset."""
103
+ return len(self.video_infos)
104
+
105
+ def _import_ground_truth(self):
106
+ """Read ground truth data from video_infos."""
107
+ ground_truth = {}
108
+ for video_info in self.video_infos:
109
+ video_id = video_info['video_name'][2:]
110
+ this_video_ground_truths = []
111
+ for ann in video_info['annotations']:
112
+ t_start, t_end = ann['segment']
113
+ label = ann['label']
114
+ this_video_ground_truths.append([t_start, t_end, label])
115
+ ground_truth[video_id] = np.array(this_video_ground_truths)
116
+ return ground_truth
117
+
118
+ @staticmethod
119
+ def proposals2json(results, show_progress=False):
120
+ """Convert all proposals to a final dict(json) format.
121
+
122
+ Args:
123
+ results (list[dict]): All proposals.
124
+ show_progress (bool): Whether to show the progress bar.
125
+ Defaults: False.
126
+
127
+ Returns:
128
+ dict: The final result dict. E.g.
129
+
130
+ .. code-block:: Python
131
+
132
+ dict(video-1=[dict(segment=[1.1,2.0]. score=0.9),
133
+ dict(segment=[50.1, 129.3], score=0.6)])
134
+ """
135
+ result_dict = {}
136
+ print('Convert proposals to json format')
137
+ if show_progress:
138
+ prog_bar = mmcv.ProgressBar(len(results))
139
+ for result in results:
140
+ video_name = result['video_name']
141
+ result_dict[video_name[2:]] = result['proposal_list']
142
+ if show_progress:
143
+ prog_bar.update()
144
+ return result_dict
145
+
146
+ @staticmethod
147
+ def _import_proposals(results):
148
+ """Read predictions from results."""
149
+ proposals = {}
150
+ num_proposals = 0
151
+ for result in results:
152
+ video_id = result['video_name'][2:]
153
+ this_video_proposals = []
154
+ for proposal in result['proposal_list']:
155
+ t_start, t_end = proposal['segment']
156
+ score = proposal['score']
157
+ this_video_proposals.append([t_start, t_end, score])
158
+ num_proposals += 1
159
+ proposals[video_id] = np.array(this_video_proposals)
160
+ return proposals, num_proposals
161
+
162
+ def dump_results(self, results, out, output_format, version='VERSION 1.3'):
163
+ """Dump data to json/csv files."""
164
+ if output_format == 'json':
165
+ result_dict = self.proposals2json(results)
166
+ output_dict = {
167
+ 'version': version,
168
+ 'results': result_dict,
169
+ 'external_data': {}
170
+ }
171
+ mmcv.dump(output_dict, out)
172
+ elif output_format == 'csv':
173
+ # TODO: add csv handler to mmcv and use mmcv.dump
174
+ os.makedirs(out, exist_ok=True)
175
+ header = 'action,start,end,tmin,tmax'
176
+ for result in results:
177
+ video_name, outputs = result
178
+ output_path = osp.join(out, video_name + '.csv')
179
+ np.savetxt(
180
+ output_path,
181
+ outputs,
182
+ header=header,
183
+ delimiter=',',
184
+ comments='')
185
+ else:
186
+ raise ValueError(
187
+ f'The output format {output_format} is not supported.')
188
+
189
+ def evaluate(
190
+ self,
191
+ results,
192
+ metrics='AR@AN',
193
+ metric_options={
194
+ 'AR@AN':
195
+ dict(
196
+ max_avg_proposals=100,
197
+ temporal_iou_thresholds=np.linspace(0.5, 0.95, 10))
198
+ },
199
+ logger=None,
200
+ **deprecated_kwargs):
201
+ """Evaluation in feature dataset.
202
+
203
+ Args:
204
+ results (list[dict]): Output results.
205
+ metrics (str | sequence[str]): Metrics to be performed.
206
+ Defaults: 'AR@AN'.
207
+ metric_options (dict): Dict for metric options. Options are
208
+ ``max_avg_proposals``, ``temporal_iou_thresholds`` for
209
+ ``AR@AN``.
210
+ default: ``{'AR@AN': dict(max_avg_proposals=100,
211
+ temporal_iou_thresholds=np.linspace(0.5, 0.95, 10))}``.
212
+ logger (logging.Logger | None): Training logger. Defaults: None.
213
+ deprecated_kwargs (dict): Used for containing deprecated arguments.
214
+ See 'https://github.com/open-mmlab/mmaction2/pull/286'.
215
+
216
+ Returns:
217
+ dict: Evaluation results for evaluation metrics.
218
+ """
219
+ # Protect ``metric_options`` since it uses mutable value as default
220
+ metric_options = copy.deepcopy(metric_options)
221
+
222
+ if deprecated_kwargs != {}:
223
+ warnings.warn(
224
+ 'Option arguments for metrics has been changed to '
225
+ "`metric_options`, See 'https://github.com/open-mmlab/mmaction2/pull/286' " # noqa: E501
226
+ 'for more details')
227
+ metric_options['AR@AN'] = dict(metric_options['AR@AN'],
228
+ **deprecated_kwargs)
229
+
230
+ if not isinstance(results, list):
231
+ raise TypeError(f'results must be a list, but got {type(results)}')
232
+ assert len(results) == len(self), (
233
+ f'The length of results is not equal to the dataset len: '
234
+ f'{len(results)} != {len(self)}')
235
+
236
+ metrics = metrics if isinstance(metrics, (list, tuple)) else [metrics]
237
+ allowed_metrics = ['AR@AN']
238
+ for metric in metrics:
239
+ if metric not in allowed_metrics:
240
+ raise KeyError(f'metric {metric} is not supported')
241
+
242
+ eval_results = OrderedDict()
243
+ ground_truth = self._import_ground_truth()
244
+ proposal, num_proposals = self._import_proposals(results)
245
+
246
+ for metric in metrics:
247
+ if metric == 'AR@AN':
248
+ temporal_iou_thresholds = metric_options.setdefault(
249
+ 'AR@AN', {}).setdefault('temporal_iou_thresholds',
250
+ np.linspace(0.5, 0.95, 10))
251
+ max_avg_proposals = metric_options.setdefault(
252
+ 'AR@AN', {}).setdefault('max_avg_proposals', 100)
253
+ if isinstance(temporal_iou_thresholds, list):
254
+ temporal_iou_thresholds = np.array(temporal_iou_thresholds)
255
+
256
+ recall, _, _, auc = (
257
+ average_recall_at_avg_proposals(
258
+ ground_truth,
259
+ proposal,
260
+ num_proposals,
261
+ max_avg_proposals=max_avg_proposals,
262
+ temporal_iou_thresholds=temporal_iou_thresholds))
263
+ eval_results['auc'] = auc
264
+ eval_results['AR@1'] = np.mean(recall[:, 0])
265
+ eval_results['AR@5'] = np.mean(recall[:, 4])
266
+ eval_results['AR@10'] = np.mean(recall[:, 9])
267
+ eval_results['AR@100'] = np.mean(recall[:, 99])
268
+
269
+ return eval_results
TransRAC/mmaction/datasets/audio_dataset.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+
3
+ import torch
4
+
5
+ from .base import BaseDataset
6
+ from .builder import DATASETS
7
+
8
+
9
+ @DATASETS.register_module()
10
+ class AudioDataset(BaseDataset):
11
+ """Audio dataset for video recognition. Extracts the audio feature on-the-
12
+ fly. Annotation file can be that of the rawframe dataset, or:
13
+
14
+ .. code-block:: txt
15
+
16
+ some/directory-1.wav 163 1
17
+ some/directory-2.wav 122 1
18
+ some/directory-3.wav 258 2
19
+ some/directory-4.wav 234 2
20
+ some/directory-5.wav 295 3
21
+ some/directory-6.wav 121 3
22
+
23
+ Args:
24
+ ann_file (str): Path to the annotation file.
25
+ pipeline (list[dict | callable]): A sequence of data transforms.
26
+ suffix (str): The suffix of the audio file. Default: '.wav'.
27
+ kwargs (dict): Other keyword args for `BaseDataset`.
28
+ """
29
+
30
+ def __init__(self, ann_file, pipeline, suffix='.wav', **kwargs):
31
+ self.suffix = suffix
32
+ super().__init__(ann_file, pipeline, modality='Audio', **kwargs)
33
+
34
+ def load_annotations(self):
35
+ """Load annotation file to get video information."""
36
+ if self.ann_file.endswith('.json'):
37
+ return self.load_json_annotations()
38
+ video_infos = []
39
+ with open(self.ann_file, 'r') as fin:
40
+ for line in fin:
41
+ line_split = line.strip().split()
42
+ video_info = {}
43
+ idx = 0
44
+ filename = line_split[idx]
45
+ if self.data_prefix is not None:
46
+ if not filename.endswith(self.suffix):
47
+ filename = osp.join(self.data_prefix,
48
+ filename + self.suffix)
49
+ else:
50
+ filename = osp.join(self.data_prefix, filename)
51
+ video_info['audio_path'] = filename
52
+ idx += 1
53
+ # idx for total_frames
54
+ video_info['total_frames'] = int(line_split[idx])
55
+ idx += 1
56
+ # idx for label[s]
57
+ label = [int(x) for x in line_split[idx:]]
58
+ assert label, f'missing label in line: {line}'
59
+ if self.multi_class:
60
+ assert self.num_classes is not None
61
+ onehot = torch.zeros(self.num_classes)
62
+ onehot[label] = 1.0
63
+ video_info['label'] = onehot
64
+ else:
65
+ assert len(label) == 1
66
+ video_info['label'] = label[0]
67
+ video_infos.append(video_info)
68
+
69
+ return video_infos
TransRAC/mmaction/datasets/audio_feature_dataset.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+
3
+ import torch
4
+
5
+ from .base import BaseDataset
6
+ from .builder import DATASETS
7
+
8
+
9
+ @DATASETS.register_module()
10
+ class AudioFeatureDataset(BaseDataset):
11
+ """Audio feature dataset for video recognition. Reads the features
12
+ extracted off-line. Annotation file can be that of the rawframe dataset,
13
+ or:
14
+
15
+ .. code-block:: txt
16
+
17
+ some/directory-1.npy 163 1
18
+ some/directory-2.npy 122 1
19
+ some/directory-3.npy 258 2
20
+ some/directory-4.npy 234 2
21
+ some/directory-5.npy 295 3
22
+ some/directory-6.npy 121 3
23
+
24
+ Args:
25
+ ann_file (str): Path to the annotation file.
26
+ pipeline (list[dict | callable]): A sequence of data transforms.
27
+ suffix (str): The suffix of the audio feature file. Default: '.npy'.
28
+ kwargs (dict): Other keyword args for `BaseDataset`.
29
+ """
30
+
31
+ def __init__(self, ann_file, pipeline, suffix='.npy', **kwargs):
32
+ self.suffix = suffix
33
+ super().__init__(ann_file, pipeline, modality='Audio', **kwargs)
34
+
35
+ def load_annotations(self):
36
+ """Load annotation file to get video information."""
37
+ if self.ann_file.endswith('.json'):
38
+ return self.load_json_annotations()
39
+ video_infos = []
40
+ with open(self.ann_file, 'r') as fin:
41
+ for line in fin:
42
+ line_split = line.strip().split()
43
+ video_info = {}
44
+ idx = 0
45
+ filename = line_split[idx]
46
+ if self.data_prefix is not None:
47
+ if not filename.endswith(self.suffix):
48
+ filename = osp.join(self.data_prefix,
49
+ filename) + self.suffix
50
+ else:
51
+ filename = osp.join(self.data_prefix, filename)
52
+ video_info['audio_path'] = filename
53
+ idx += 1
54
+ # idx for total_frames
55
+ video_info['total_frames'] = int(line_split[idx])
56
+ idx += 1
57
+ # idx for label[s]
58
+ label = [int(x) for x in line_split[idx:]]
59
+ assert label, f'missing label in line: {line}'
60
+ if self.multi_class:
61
+ assert self.num_classes is not None
62
+ onehot = torch.zeros(self.num_classes)
63
+ onehot[label] = 1.0
64
+ video_info['label'] = onehot
65
+ else:
66
+ assert len(label) == 1
67
+ video_info['label'] = label[0]
68
+ video_infos.append(video_info)
69
+
70
+ return video_infos
TransRAC/mmaction/datasets/audio_visual_dataset.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+
3
+ from .builder import DATASETS
4
+ from .rawframe_dataset import RawframeDataset
5
+
6
+
7
+ @DATASETS.register_module()
8
+ class AudioVisualDataset(RawframeDataset):
9
+ """Dataset that reads both audio and visual data, supporting both rawframes
10
+ and videos. The annotation file is same as that of the rawframe dataset,
11
+ such as:
12
+
13
+ .. code-block:: txt
14
+
15
+ some/directory-1 163 1
16
+ some/directory-2 122 1
17
+ some/directory-3 258 2
18
+ some/directory-4 234 2
19
+ some/directory-5 295 3
20
+ some/directory-6 121 3
21
+
22
+ Args:
23
+ ann_file (str): Path to the annotation file.
24
+ pipeline (list[dict | callable]): A sequence of data transforms.
25
+ audio_prefix (str): Directory of the audio files.
26
+ kwargs (dict): Other keyword args for `RawframeDataset`. `video_prefix`
27
+ is also allowed if pipeline is designed for videos.
28
+ """
29
+
30
+ def __init__(self, ann_file, pipeline, audio_prefix, **kwargs):
31
+ self.audio_prefix = audio_prefix
32
+ self.video_prefix = kwargs.pop('video_prefix', None)
33
+ self.data_prefix = kwargs.get('data_prefix', None)
34
+ super().__init__(ann_file, pipeline, **kwargs)
35
+
36
+ def load_annotations(self):
37
+ video_infos = []
38
+ with open(self.ann_file, 'r') as fin:
39
+ for line in fin:
40
+ line_split = line.strip().split()
41
+ video_info = {}
42
+ idx = 0
43
+ # idx for frame_dir
44
+ frame_dir = line_split[idx]
45
+ if self.audio_prefix is not None:
46
+ audio_path = osp.join(self.audio_prefix,
47
+ frame_dir + '.npy')
48
+ video_info['audio_path'] = audio_path
49
+ if self.video_prefix:
50
+ video_path = osp.join(self.video_prefix,
51
+ frame_dir + '.mp4')
52
+ video_info['filename'] = video_path
53
+ if self.data_prefix is not None:
54
+ frame_dir = osp.join(self.data_prefix, frame_dir)
55
+ video_info['frame_dir'] = frame_dir
56
+ idx += 1
57
+ if self.with_offset:
58
+ # idx for offset and total_frames
59
+ video_info['offset'] = int(line_split[idx])
60
+ video_info['total_frames'] = int(line_split[idx + 1])
61
+ idx += 2
62
+ else:
63
+ # idx for total_frames
64
+ video_info['total_frames'] = int(line_split[idx])
65
+ idx += 1
66
+ # idx for label[s]
67
+ label = [int(x) for x in line_split[idx:]]
68
+ assert len(label) != 0, f'missing label in line: {line}'
69
+ if self.multi_class:
70
+ assert self.num_classes is not None
71
+ video_info['label'] = label
72
+ else:
73
+ assert len(label) == 1
74
+ video_info['label'] = label[0]
75
+ video_infos.append(video_info)
76
+ return video_infos
TransRAC/mmaction/datasets/ava_dataset.py ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os
3
+ import os.path as osp
4
+ from collections import defaultdict
5
+ from datetime import datetime
6
+
7
+ import mmcv
8
+ import numpy as np
9
+ from mmcv.utils import print_log
10
+
11
+ from ..core.evaluation.ava_utils import ava_eval, read_labelmap, results2csv
12
+ from ..utils import get_root_logger
13
+ from .base import BaseDataset
14
+ from .builder import DATASETS
15
+
16
+
17
+ @DATASETS.register_module()
18
+ class AVADataset(BaseDataset):
19
+ """AVA dataset for spatial temporal detection.
20
+
21
+ Based on official AVA annotation files, the dataset loads raw frames,
22
+ bounding boxes, proposals and applies specified transformations to return
23
+ a dict containing the frame tensors and other information.
24
+
25
+ This datasets can load information from the following files:
26
+
27
+ .. code-block:: txt
28
+
29
+ ann_file -> ava_{train, val}_{v2.1, v2.2}.csv
30
+ exclude_file -> ava_{train, val}_excluded_timestamps_{v2.1, v2.2}.csv
31
+ label_file -> ava_action_list_{v2.1, v2.2}.pbtxt /
32
+ ava_action_list_{v2.1, v2.2}_for_activitynet_2019.pbtxt
33
+ proposal_file -> ava_dense_proposals_{train, val}.FAIR.recall_93.9.pkl
34
+
35
+ Particularly, the proposal_file is a pickle file which contains
36
+ ``img_key`` (in format of ``{video_id},{timestamp}``). Example of a pickle
37
+ file:
38
+
39
+ .. code-block:: JSON
40
+
41
+ {
42
+ ...
43
+ '0f39OWEqJ24,0902':
44
+ array([[0.011 , 0.157 , 0.655 , 0.983 , 0.998163]]),
45
+ '0f39OWEqJ24,0912':
46
+ array([[0.054 , 0.088 , 0.91 , 0.998 , 0.068273],
47
+ [0.016 , 0.161 , 0.519 , 0.974 , 0.984025],
48
+ [0.493 , 0.283 , 0.981 , 0.984 , 0.983621]]),
49
+ ...
50
+ }
51
+
52
+ Args:
53
+ ann_file (str): Path to the annotation file like
54
+ ``ava_{train, val}_{v2.1, v2.2}.csv``.
55
+ exclude_file (str): Path to the excluded timestamp file like
56
+ ``ava_{train, val}_excluded_timestamps_{v2.1, v2.2}.csv``.
57
+ pipeline (list[dict | callable]): A sequence of data transforms.
58
+ label_file (str): Path to the label file like
59
+ ``ava_action_list_{v2.1, v2.2}.pbtxt`` or
60
+ ``ava_action_list_{v2.1, v2.2}_for_activitynet_2019.pbtxt``.
61
+ Default: None.
62
+ filename_tmpl (str): Template for each filename.
63
+ Default: 'img_{:05}.jpg'.
64
+ proposal_file (str): Path to the proposal file like
65
+ ``ava_dense_proposals_{train, val}.FAIR.recall_93.9.pkl``.
66
+ Default: None.
67
+ person_det_score_thr (float): The threshold of person detection scores,
68
+ bboxes with scores above the threshold will be used. Default: 0.9.
69
+ Note that 0 <= person_det_score_thr <= 1. If no proposal has
70
+ detection score larger than the threshold, the one with the largest
71
+ detection score will be used.
72
+ num_classes (int): The number of classes of the dataset. Default: 81.
73
+ (AVA has 80 action classes, another 1-dim is added for potential
74
+ usage)
75
+ custom_classes (list[int]): A subset of class ids from origin dataset.
76
+ Please note that 0 should NOT be selected, and ``num_classes``
77
+ should be equal to ``len(custom_classes) + 1``
78
+ data_prefix (str): Path to a directory where videos are held.
79
+ Default: None.
80
+ test_mode (bool): Store True when building test or validation dataset.
81
+ Default: False.
82
+ modality (str): Modality of data. Support 'RGB', 'Flow'.
83
+ Default: 'RGB'.
84
+ num_max_proposals (int): Max proposals number to store. Default: 1000.
85
+ timestamp_start (int): The start point of included timestamps. The
86
+ default value is referred from the official website. Default: 902.
87
+ timestamp_end (int): The end point of included timestamps. The
88
+ default value is referred from the official website. Default: 1798.
89
+ """
90
+
91
+ _FPS = 30
92
+
93
+ def __init__(self,
94
+ ann_file,
95
+ exclude_file,
96
+ pipeline,
97
+ label_file=None,
98
+ filename_tmpl='img_{:05}.jpg',
99
+ proposal_file=None,
100
+ person_det_score_thr=0.9,
101
+ num_classes=81,
102
+ custom_classes=None,
103
+ data_prefix=None,
104
+ test_mode=False,
105
+ modality='RGB',
106
+ num_max_proposals=1000,
107
+ timestamp_start=900,
108
+ timestamp_end=1800):
109
+ # since it inherits from `BaseDataset`, some arguments
110
+ # should be assigned before performing `load_annotations()`
111
+ self.custom_classes = custom_classes
112
+ if custom_classes is not None:
113
+ assert num_classes == len(custom_classes) + 1
114
+ assert 0 not in custom_classes
115
+ _, class_whitelist = read_labelmap(open(label_file))
116
+ assert set(custom_classes).issubset(class_whitelist)
117
+
118
+ self.custom_classes = tuple([0] + custom_classes)
119
+ self.exclude_file = exclude_file
120
+ self.label_file = label_file
121
+ self.proposal_file = proposal_file
122
+ assert 0 <= person_det_score_thr <= 1, (
123
+ 'The value of '
124
+ 'person_det_score_thr should in [0, 1]. ')
125
+ self.person_det_score_thr = person_det_score_thr
126
+ self.num_classes = num_classes
127
+ self.filename_tmpl = filename_tmpl
128
+ self.num_max_proposals = num_max_proposals
129
+ self.timestamp_start = timestamp_start
130
+ self.timestamp_end = timestamp_end
131
+ self.logger = get_root_logger()
132
+ super().__init__(
133
+ ann_file,
134
+ pipeline,
135
+ data_prefix,
136
+ test_mode,
137
+ modality=modality,
138
+ num_classes=num_classes)
139
+
140
+ if self.proposal_file is not None:
141
+ self.proposals = mmcv.load(self.proposal_file)
142
+ else:
143
+ self.proposals = None
144
+
145
+ if not test_mode:
146
+ valid_indexes = self.filter_exclude_file()
147
+ self.logger.info(
148
+ f'{len(valid_indexes)} out of {len(self.video_infos)} '
149
+ f'frames are valid.')
150
+ self.video_infos = [self.video_infos[i] for i in valid_indexes]
151
+
152
+ def parse_img_record(self, img_records):
153
+ """Merge image records of the same entity at the same time.
154
+
155
+ Args:
156
+ img_records (list[dict]): List of img_records (lines in AVA
157
+ annotations).
158
+
159
+ Returns:
160
+ tuple(list): A tuple consists of lists of bboxes, action labels and
161
+ entity_ids
162
+ """
163
+ bboxes, labels, entity_ids = [], [], []
164
+ while len(img_records) > 0:
165
+ img_record = img_records[0]
166
+ num_img_records = len(img_records)
167
+ selected_records = list(
168
+ filter(
169
+ lambda x: np.array_equal(x['entity_box'], img_record[
170
+ 'entity_box']), img_records))
171
+ num_selected_records = len(selected_records)
172
+ img_records = list(
173
+ filter(
174
+ lambda x: not np.array_equal(x['entity_box'], img_record[
175
+ 'entity_box']), img_records))
176
+ assert len(img_records) + num_selected_records == num_img_records
177
+
178
+ bboxes.append(img_record['entity_box'])
179
+ valid_labels = np.array([
180
+ selected_record['label']
181
+ for selected_record in selected_records
182
+ ])
183
+
184
+ # The format can be directly used by BCELossWithLogits
185
+ label = np.zeros(self.num_classes, dtype=np.float32)
186
+ label[valid_labels] = 1.
187
+
188
+ labels.append(label)
189
+ entity_ids.append(img_record['entity_id'])
190
+
191
+ bboxes = np.stack(bboxes)
192
+ labels = np.stack(labels)
193
+ entity_ids = np.stack(entity_ids)
194
+ return bboxes, labels, entity_ids
195
+
196
+ def filter_exclude_file(self):
197
+ """Filter out records in the exclude_file."""
198
+ valid_indexes = []
199
+ if self.exclude_file is None:
200
+ valid_indexes = list(range(len(self.video_infos)))
201
+ else:
202
+ exclude_video_infos = [
203
+ x.strip().split(',') for x in open(self.exclude_file)
204
+ ]
205
+ for i, video_info in enumerate(self.video_infos):
206
+ valid_indexes.append(i)
207
+ for video_id, timestamp in exclude_video_infos:
208
+ if (video_info['video_id'] == video_id
209
+ and video_info['timestamp'] == int(timestamp)):
210
+ valid_indexes.pop()
211
+ break
212
+ return valid_indexes
213
+
214
+ def load_annotations(self):
215
+ """Load AVA annotations."""
216
+ video_infos = []
217
+ records_dict_by_img = defaultdict(list)
218
+ with open(self.ann_file, 'r') as fin:
219
+ for line in fin:
220
+ line_split = line.strip().split(',')
221
+
222
+ label = int(line_split[6])
223
+ if self.custom_classes is not None:
224
+ if label not in self.custom_classes:
225
+ continue
226
+ label = self.custom_classes.index(label)
227
+
228
+ video_id = line_split[0]
229
+ timestamp = int(line_split[1])
230
+ img_key = f'{video_id},{timestamp:04d}'
231
+
232
+ entity_box = np.array(list(map(float, line_split[2:6])))
233
+ entity_id = int(line_split[7])
234
+ shot_info = (0, (self.timestamp_end - self.timestamp_start) *
235
+ self._FPS)
236
+
237
+ video_info = dict(
238
+ video_id=video_id,
239
+ timestamp=timestamp,
240
+ entity_box=entity_box,
241
+ label=label,
242
+ entity_id=entity_id,
243
+ shot_info=shot_info)
244
+ records_dict_by_img[img_key].append(video_info)
245
+
246
+ for img_key in records_dict_by_img:
247
+ video_id, timestamp = img_key.split(',')
248
+ bboxes, labels, entity_ids = self.parse_img_record(
249
+ records_dict_by_img[img_key])
250
+ ann = dict(
251
+ gt_bboxes=bboxes, gt_labels=labels, entity_ids=entity_ids)
252
+ frame_dir = video_id
253
+ if self.data_prefix is not None:
254
+ frame_dir = osp.join(self.data_prefix, frame_dir)
255
+ video_info = dict(
256
+ frame_dir=frame_dir,
257
+ video_id=video_id,
258
+ timestamp=int(timestamp),
259
+ img_key=img_key,
260
+ shot_info=shot_info,
261
+ fps=self._FPS,
262
+ ann=ann)
263
+ video_infos.append(video_info)
264
+
265
+ return video_infos
266
+
267
+ def prepare_train_frames(self, idx):
268
+ """Prepare the frames for training given the index."""
269
+ results = copy.deepcopy(self.video_infos[idx])
270
+ img_key = results['img_key']
271
+
272
+ results['filename_tmpl'] = self.filename_tmpl
273
+ results['modality'] = self.modality
274
+ results['start_index'] = self.start_index
275
+ results['timestamp_start'] = self.timestamp_start
276
+ results['timestamp_end'] = self.timestamp_end
277
+
278
+ if self.proposals is not None:
279
+ if img_key not in self.proposals:
280
+ results['proposals'] = np.array([[0, 0, 1, 1]])
281
+ results['scores'] = np.array([1])
282
+ else:
283
+ proposals = self.proposals[img_key]
284
+ assert proposals.shape[-1] in [4, 5]
285
+ if proposals.shape[-1] == 5:
286
+ thr = min(self.person_det_score_thr, max(proposals[:, 4]))
287
+ positive_inds = (proposals[:, 4] >= thr)
288
+ proposals = proposals[positive_inds]
289
+ proposals = proposals[:self.num_max_proposals]
290
+ results['proposals'] = proposals[:, :4]
291
+ results['scores'] = proposals[:, 4]
292
+ else:
293
+ proposals = proposals[:self.num_max_proposals]
294
+ results['proposals'] = proposals
295
+
296
+ ann = results.pop('ann')
297
+ results['gt_bboxes'] = ann['gt_bboxes']
298
+ results['gt_labels'] = ann['gt_labels']
299
+ results['entity_ids'] = ann['entity_ids']
300
+
301
+ return self.pipeline(results)
302
+
303
+ def prepare_test_frames(self, idx):
304
+ """Prepare the frames for testing given the index."""
305
+ results = copy.deepcopy(self.video_infos[idx])
306
+ img_key = results['img_key']
307
+
308
+ results['filename_tmpl'] = self.filename_tmpl
309
+ results['modality'] = self.modality
310
+ results['start_index'] = self.start_index
311
+ results['timestamp_start'] = self.timestamp_start
312
+ results['timestamp_end'] = self.timestamp_end
313
+
314
+ if self.proposals is not None:
315
+ if img_key not in self.proposals:
316
+ results['proposals'] = np.array([[0, 0, 1, 1]])
317
+ results['scores'] = np.array([1])
318
+ else:
319
+ proposals = self.proposals[img_key]
320
+ assert proposals.shape[-1] in [4, 5]
321
+ if proposals.shape[-1] == 5:
322
+ thr = min(self.person_det_score_thr, max(proposals[:, 4]))
323
+ positive_inds = (proposals[:, 4] >= thr)
324
+ proposals = proposals[positive_inds]
325
+ proposals = proposals[:self.num_max_proposals]
326
+ results['proposals'] = proposals[:, :4]
327
+ results['scores'] = proposals[:, 4]
328
+ else:
329
+ proposals = proposals[:self.num_max_proposals]
330
+ results['proposals'] = proposals
331
+
332
+ ann = results.pop('ann')
333
+ # Follow the mmdet variable naming style.
334
+ results['gt_bboxes'] = ann['gt_bboxes']
335
+ results['gt_labels'] = ann['gt_labels']
336
+ results['entity_ids'] = ann['entity_ids']
337
+
338
+ return self.pipeline(results)
339
+
340
+ def dump_results(self, results, out):
341
+ """Dump predictions into a csv file."""
342
+ assert out.endswith('csv')
343
+ results2csv(self, results, out, self.custom_classes)
344
+
345
+ def evaluate(self,
346
+ results,
347
+ metrics=('mAP', ),
348
+ metric_options=None,
349
+ logger=None):
350
+ """Evaluate the prediction results and report mAP."""
351
+ assert len(metrics) == 1 and metrics[0] == 'mAP', (
352
+ 'For evaluation on AVADataset, you need to use metrics "mAP" '
353
+ 'See https://github.com/open-mmlab/mmaction2/pull/567 '
354
+ 'for more info.')
355
+ time_now = datetime.now().strftime('%Y%m%d_%H%M%S')
356
+ temp_file = f'AVA_{time_now}_result.csv'
357
+ results2csv(self, results, temp_file, self.custom_classes)
358
+
359
+ ret = {}
360
+ for metric in metrics:
361
+ msg = f'Evaluating {metric} ...'
362
+ if logger is None:
363
+ msg = '\n' + msg
364
+ print_log(msg, logger=logger)
365
+
366
+ eval_result = ava_eval(
367
+ temp_file,
368
+ metric,
369
+ self.label_file,
370
+ self.ann_file,
371
+ self.exclude_file,
372
+ custom_classes=self.custom_classes)
373
+ log_msg = []
374
+ for k, v in eval_result.items():
375
+ log_msg.append(f'\n{k}\t{v: .4f}')
376
+ log_msg = ''.join(log_msg)
377
+ print_log(log_msg, logger=logger)
378
+ ret.update(eval_result)
379
+
380
+ os.remove(temp_file)
381
+
382
+ return ret
TransRAC/mmaction/datasets/base.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os.path as osp
3
+ import warnings
4
+ from abc import ABCMeta, abstractmethod
5
+ from collections import OrderedDict, defaultdict
6
+
7
+ import mmcv
8
+ import numpy as np
9
+ import torch
10
+ from mmcv.utils import print_log
11
+ from torch.utils.data import Dataset
12
+
13
+ from ..core import (mean_average_precision, mean_class_accuracy,
14
+ mmit_mean_average_precision, top_k_accuracy)
15
+ from .pipelines import Compose
16
+
17
+
18
+ class BaseDataset(Dataset, metaclass=ABCMeta):
19
+ """Base class for datasets.
20
+
21
+ All datasets to process video should subclass it.
22
+ All subclasses should overwrite:
23
+
24
+ - Methods:`load_annotations`, supporting to load information from an
25
+ annotation file.
26
+ - Methods:`prepare_train_frames`, providing train data.
27
+ - Methods:`prepare_test_frames`, providing test data.
28
+
29
+ Args:
30
+ ann_file (str): Path to the annotation file.
31
+ pipeline (list[dict | callable]): A sequence of data transforms.
32
+ data_prefix (str | None): Path to a directory where videos are held.
33
+ Default: None.
34
+ test_mode (bool): Store True when building test or validation dataset.
35
+ Default: False.
36
+ multi_class (bool): Determines whether the dataset is a multi-class
37
+ dataset. Default: False.
38
+ num_classes (int | None): Number of classes of the dataset, used in
39
+ multi-class datasets. Default: None.
40
+ start_index (int): Specify a start index for frames in consideration of
41
+ different filename format. However, when taking videos as input,
42
+ it should be set to 0, since frames loaded from videos count
43
+ from 0. Default: 1.
44
+ modality (str): Modality of data. Support 'RGB', 'Flow', 'Audio'.
45
+ Default: 'RGB'.
46
+ sample_by_class (bool): Sampling by class, should be set `True` when
47
+ performing inter-class data balancing. Only compatible with
48
+ `multi_class == False`. Only applies for training. Default: False.
49
+ power (float): We support sampling data with the probability
50
+ proportional to the power of its label frequency (freq ^ power)
51
+ when sampling data. `power == 1` indicates uniformly sampling all
52
+ data; `power == 0` indicates uniformly sampling all classes.
53
+ Default: 0.
54
+ dynamic_length (bool): If the dataset length is dynamic (used by
55
+ ClassSpecificDistributedSampler). Default: False.
56
+ """
57
+
58
+ def __init__(self,
59
+ ann_file,
60
+ pipeline,
61
+ data_prefix=None,
62
+ test_mode=False,
63
+ multi_class=False,
64
+ num_classes=None,
65
+ start_index=1,
66
+ modality='RGB',
67
+ sample_by_class=False,
68
+ power=0,
69
+ dynamic_length=False):
70
+ super().__init__()
71
+
72
+ self.ann_file = ann_file
73
+ self.data_prefix = osp.realpath(
74
+ data_prefix) if data_prefix is not None and osp.isdir(
75
+ data_prefix) else data_prefix
76
+ self.test_mode = test_mode
77
+ self.multi_class = multi_class
78
+ self.num_classes = num_classes
79
+ self.start_index = start_index
80
+ self.modality = modality
81
+ self.sample_by_class = sample_by_class
82
+ self.power = power
83
+ self.dynamic_length = dynamic_length
84
+
85
+ assert not (self.multi_class and self.sample_by_class)
86
+
87
+ self.pipeline = Compose(pipeline)
88
+ self.video_infos = self.load_annotations()
89
+ if self.sample_by_class:
90
+ self.video_infos_by_class = self.parse_by_class()
91
+
92
+ class_prob = []
93
+ for _, samples in self.video_infos_by_class.items():
94
+ class_prob.append(len(samples) / len(self.video_infos))
95
+ class_prob = [x**self.power for x in class_prob]
96
+
97
+ summ = sum(class_prob)
98
+ class_prob = [x / summ for x in class_prob]
99
+
100
+ self.class_prob = dict(zip(self.video_infos_by_class, class_prob))
101
+
102
+ @abstractmethod
103
+ def load_annotations(self):
104
+ """Load the annotation according to ann_file into video_infos."""
105
+
106
+ # json annotations already looks like video_infos, so for each dataset,
107
+ # this func should be the same
108
+ def load_json_annotations(self):
109
+ """Load json annotation file to get video information."""
110
+ video_infos = mmcv.load(self.ann_file)
111
+ num_videos = len(video_infos)
112
+ path_key = 'frame_dir' if 'frame_dir' in video_infos[0] else 'filename'
113
+ for i in range(num_videos):
114
+ path_value = video_infos[i][path_key]
115
+ if self.data_prefix is not None:
116
+ path_value = osp.join(self.data_prefix, path_value)
117
+ video_infos[i][path_key] = path_value
118
+ if self.multi_class:
119
+ assert self.num_classes is not None
120
+ else:
121
+ assert len(video_infos[i]['label']) == 1
122
+ video_infos[i]['label'] = video_infos[i]['label'][0]
123
+ return video_infos
124
+
125
+ def parse_by_class(self):
126
+ video_infos_by_class = defaultdict(list)
127
+ for item in self.video_infos:
128
+ label = item['label']
129
+ video_infos_by_class[label].append(item)
130
+ return video_infos_by_class
131
+
132
+ @staticmethod
133
+ def label2array(num, label):
134
+ arr = np.zeros(num, dtype=np.float32)
135
+ arr[label] = 1.
136
+ return arr
137
+
138
+ def evaluate(self,
139
+ results,
140
+ metrics='top_k_accuracy',
141
+ metric_options=dict(top_k_accuracy=dict(topk=(1, 5))),
142
+ logger=None,
143
+ **deprecated_kwargs):
144
+ """Perform evaluation for common datasets.
145
+
146
+ Args:
147
+ results (list): Output results.
148
+ metrics (str | sequence[str]): Metrics to be performed.
149
+ Defaults: 'top_k_accuracy'.
150
+ metric_options (dict): Dict for metric options. Options are
151
+ ``topk`` for ``top_k_accuracy``.
152
+ Default: ``dict(top_k_accuracy=dict(topk=(1, 5)))``.
153
+ logger (logging.Logger | None): Logger for recording.
154
+ Default: None.
155
+ deprecated_kwargs (dict): Used for containing deprecated arguments.
156
+ See 'https://github.com/open-mmlab/mmaction2/pull/286'.
157
+
158
+ Returns:
159
+ dict: Evaluation results dict.
160
+ """
161
+ # Protect ``metric_options`` since it uses mutable value as default
162
+ metric_options = copy.deepcopy(metric_options)
163
+
164
+ if deprecated_kwargs != {}:
165
+ warnings.warn(
166
+ 'Option arguments for metrics has been changed to '
167
+ "`metric_options`, See 'https://github.com/open-mmlab/mmaction2/pull/286' " # noqa: E501
168
+ 'for more details')
169
+ metric_options['top_k_accuracy'] = dict(
170
+ metric_options['top_k_accuracy'], **deprecated_kwargs)
171
+
172
+ if not isinstance(results, list):
173
+ raise TypeError(f'results must be a list, but got {type(results)}')
174
+ assert len(results) == len(self), (
175
+ f'The length of results is not equal to the dataset len: '
176
+ f'{len(results)} != {len(self)}')
177
+
178
+ metrics = metrics if isinstance(metrics, (list, tuple)) else [metrics]
179
+ allowed_metrics = [
180
+ 'top_k_accuracy', 'mean_class_accuracy', 'mean_average_precision',
181
+ 'mmit_mean_average_precision'
182
+ ]
183
+
184
+ for metric in metrics:
185
+ if metric not in allowed_metrics:
186
+ raise KeyError(f'metric {metric} is not supported')
187
+
188
+ eval_results = OrderedDict()
189
+ gt_labels = [ann['label'] for ann in self.video_infos]
190
+
191
+ for metric in metrics:
192
+ msg = f'Evaluating {metric} ...'
193
+ if logger is None:
194
+ msg = '\n' + msg
195
+ print_log(msg, logger=logger)
196
+
197
+ if metric == 'top_k_accuracy':
198
+ topk = metric_options.setdefault('top_k_accuracy',
199
+ {}).setdefault(
200
+ 'topk', (1, 5))
201
+ if not isinstance(topk, (int, tuple)):
202
+ raise TypeError('topk must be int or tuple of int, '
203
+ f'but got {type(topk)}')
204
+ if isinstance(topk, int):
205
+ topk = (topk, )
206
+
207
+ top_k_acc = top_k_accuracy(results, gt_labels, topk)
208
+ log_msg = []
209
+ for k, acc in zip(topk, top_k_acc):
210
+ eval_results[f'top{k}_acc'] = acc
211
+ log_msg.append(f'\ntop{k}_acc\t{acc:.4f}')
212
+ log_msg = ''.join(log_msg)
213
+ print_log(log_msg, logger=logger)
214
+ continue
215
+
216
+ if metric == 'mean_class_accuracy':
217
+ mean_acc = mean_class_accuracy(results, gt_labels)
218
+ eval_results['mean_class_accuracy'] = mean_acc
219
+ log_msg = f'\nmean_acc\t{mean_acc:.4f}'
220
+ print_log(log_msg, logger=logger)
221
+ continue
222
+
223
+ if metric in [
224
+ 'mean_average_precision', 'mmit_mean_average_precision'
225
+ ]:
226
+ gt_labels = [
227
+ self.label2array(self.num_classes, label)
228
+ for label in gt_labels
229
+ ]
230
+ if metric == 'mean_average_precision':
231
+ mAP = mean_average_precision(results, gt_labels)
232
+ eval_results['mean_average_precision'] = mAP
233
+ log_msg = f'\nmean_average_precision\t{mAP:.4f}'
234
+ elif metric == 'mmit_mean_average_precision':
235
+ mAP = mmit_mean_average_precision(results, gt_labels)
236
+ eval_results['mmit_mean_average_precision'] = mAP
237
+ log_msg = f'\nmmit_mean_average_precision\t{mAP:.4f}'
238
+ print_log(log_msg, logger=logger)
239
+ continue
240
+
241
+ return eval_results
242
+
243
+ @staticmethod
244
+ def dump_results(results, out):
245
+ """Dump data to json/yaml/pickle strings or files."""
246
+ return mmcv.dump(results, out)
247
+
248
+ def prepare_train_frames(self, idx):
249
+ """Prepare the frames for training given the index."""
250
+ results = copy.deepcopy(self.video_infos[idx])
251
+ results['modality'] = self.modality
252
+ results['start_index'] = self.start_index
253
+
254
+ # prepare tensor in getitem
255
+ # If HVU, type(results['label']) is dict
256
+ if self.multi_class and isinstance(results['label'], list):
257
+ onehot = torch.zeros(self.num_classes)
258
+ onehot[results['label']] = 1.
259
+ results['label'] = onehot
260
+
261
+ return self.pipeline(results)
262
+
263
+ def prepare_test_frames(self, idx):
264
+ """Prepare the frames for testing given the index."""
265
+ results = copy.deepcopy(self.video_infos[idx])
266
+ results['modality'] = self.modality
267
+ results['start_index'] = self.start_index
268
+
269
+ # prepare tensor in getitem
270
+ # If HVU, type(results['label']) is dict
271
+ if self.multi_class and isinstance(results['label'], list):
272
+ onehot = torch.zeros(self.num_classes)
273
+ onehot[results['label']] = 1.
274
+ results['label'] = onehot
275
+
276
+ return self.pipeline(results)
277
+
278
+ def __len__(self):
279
+ """Get the size of the dataset."""
280
+ return len(self.video_infos)
281
+
282
+ def __getitem__(self, idx):
283
+ """Get the sample for either training or testing given index."""
284
+ if self.test_mode:
285
+ return self.prepare_test_frames(idx)
286
+
287
+ return self.prepare_train_frames(idx)
TransRAC/mmaction/datasets/builder.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import platform
2
+ import random
3
+ from functools import partial
4
+
5
+ import numpy as np
6
+ from mmcv.parallel import collate
7
+ from mmcv.runner import get_dist_info
8
+ from mmcv.utils import Registry, build_from_cfg
9
+ from torch.utils.data import DataLoader
10
+
11
+ from .samplers import ClassSpecificDistributedSampler, DistributedSampler
12
+
13
+ if platform.system() != 'Windows':
14
+ # https://github.com/pytorch/pytorch/issues/973
15
+ import resource
16
+ rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
17
+ hard_limit = rlimit[1]
18
+ soft_limit = min(4096, hard_limit)
19
+ resource.setrlimit(resource.RLIMIT_NOFILE, (soft_limit, hard_limit))
20
+
21
+ DATASETS = Registry('dataset')
22
+ PIPELINES = Registry('pipeline')
23
+ BLENDINGS = Registry('blending')
24
+
25
+
26
+ def build_dataset(cfg, default_args=None):
27
+ """Build a dataset from config dict.
28
+
29
+ Args:
30
+ cfg (dict): Config dict. It should at least contain the key "type".
31
+ default_args (dict | None, optional): Default initialization arguments.
32
+ Default: None.
33
+
34
+ Returns:
35
+ Dataset: The constructed dataset.
36
+ """
37
+ if cfg['type'] == 'RepeatDataset':
38
+ from .dataset_wrappers import RepeatDataset
39
+ dataset = RepeatDataset(
40
+ build_dataset(cfg['dataset'], default_args), cfg['times'])
41
+ else:
42
+ dataset = build_from_cfg(cfg, DATASETS, default_args)
43
+ return dataset
44
+
45
+
46
+ def build_dataloader(dataset,
47
+ videos_per_gpu,
48
+ workers_per_gpu,
49
+ num_gpus=1,
50
+ dist=True,
51
+ shuffle=True,
52
+ seed=None,
53
+ drop_last=False,
54
+ pin_memory=True,
55
+ **kwargs):
56
+ """Build PyTorch DataLoader.
57
+
58
+ In distributed training, each GPU/process has a dataloader.
59
+ In non-distributed training, there is only one dataloader for all GPUs.
60
+
61
+ Args:
62
+ dataset (:obj:`Dataset`): A PyTorch dataset.
63
+ videos_per_gpu (int): Number of videos on each GPU, i.e.,
64
+ batch size of each GPU.
65
+ workers_per_gpu (int): How many subprocesses to use for data
66
+ loading for each GPU.
67
+ num_gpus (int): Number of GPUs. Only used in non-distributed
68
+ training. Default: 1.
69
+ dist (bool): Distributed training/test or not. Default: True.
70
+ shuffle (bool): Whether to shuffle the data at every epoch.
71
+ Default: True.
72
+ seed (int | None): Seed to be used. Default: None.
73
+ drop_last (bool): Whether to drop the last incomplete batch in epoch.
74
+ Default: False
75
+ pin_memory (bool): Whether to use pin_memory in DataLoader.
76
+ Default: True
77
+ kwargs (dict, optional): Any keyword argument to be used to initialize
78
+ DataLoader.
79
+
80
+ Returns:
81
+ DataLoader: A PyTorch dataloader.
82
+ """
83
+ rank, world_size = get_dist_info()
84
+ sample_by_class = getattr(dataset, 'sample_by_class', False)
85
+
86
+ if dist:
87
+ if sample_by_class:
88
+ dynamic_length = getattr(dataset, 'dynamic_length', True)
89
+ sampler = ClassSpecificDistributedSampler(
90
+ dataset,
91
+ world_size,
92
+ rank,
93
+ dynamic_length=dynamic_length,
94
+ shuffle=shuffle,
95
+ seed=seed)
96
+ else:
97
+ sampler = DistributedSampler(
98
+ dataset, world_size, rank, shuffle=shuffle, seed=seed)
99
+ shuffle = False
100
+ batch_size = videos_per_gpu
101
+ num_workers = workers_per_gpu
102
+ else:
103
+ sampler = None
104
+ batch_size = num_gpus * videos_per_gpu
105
+ num_workers = num_gpus * workers_per_gpu
106
+
107
+ init_fn = partial(
108
+ worker_init_fn, num_workers=num_workers, rank=rank,
109
+ seed=seed) if seed is not None else None
110
+
111
+ data_loader = DataLoader(
112
+ dataset,
113
+ batch_size=batch_size,
114
+ sampler=sampler,
115
+ num_workers=num_workers,
116
+ collate_fn=partial(collate, samples_per_gpu=videos_per_gpu),
117
+ pin_memory=pin_memory,
118
+ shuffle=shuffle,
119
+ worker_init_fn=init_fn,
120
+ drop_last=drop_last,
121
+ **kwargs)
122
+
123
+ return data_loader
124
+
125
+
126
+ def worker_init_fn(worker_id, num_workers, rank, seed):
127
+ """Init the random seed for various workers."""
128
+ # The seed of each worker equals to
129
+ # num_worker * rank + worker_id + user_seed
130
+ worker_seed = num_workers * rank + worker_id + seed
131
+ np.random.seed(worker_seed)
132
+ random.seed(worker_seed)
TransRAC/mmaction/datasets/dataset_wrappers.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .builder import DATASETS
2
+
3
+
4
+ @DATASETS.register_module()
5
+ class RepeatDataset:
6
+ """A wrapper of repeated dataset.
7
+
8
+ The length of repeated dataset will be ``times`` larger than the original
9
+ dataset. This is useful when the data loading time is long but the dataset
10
+ is small. Using RepeatDataset can reduce the data loading time between
11
+ epochs.
12
+
13
+ Args:
14
+ dataset (:obj:`Dataset`): The dataset to be repeated.
15
+ times (int): Repeat times.
16
+ """
17
+
18
+ def __init__(self, dataset, times):
19
+ self.dataset = dataset
20
+ self.times = times
21
+
22
+ self._ori_len = len(self.dataset)
23
+
24
+ def __getitem__(self, idx):
25
+ """Get data."""
26
+ return self.dataset[idx % self._ori_len]
27
+
28
+ def __len__(self):
29
+ """Length after repetition."""
30
+ return self.times * self._ori_len
TransRAC/mmaction/datasets/hvu_dataset.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os.path as osp
3
+ from collections import OrderedDict
4
+
5
+ import mmcv
6
+ import numpy as np
7
+ from mmcv.utils import print_log
8
+
9
+ from ..core import mean_average_precision
10
+ from .base import BaseDataset
11
+ from .builder import DATASETS
12
+
13
+
14
+ @DATASETS.register_module()
15
+ class HVUDataset(BaseDataset):
16
+ """HVU dataset, which supports the recognition tags of multiple categories.
17
+ Accept both video annotation files or rawframe annotation files.
18
+
19
+ The dataset loads videos or raw frames and applies specified transforms to
20
+ return a dict containing the frame tensors and other information.
21
+
22
+ The ann_file is a json file with multiple dictionaries, and each dictionary
23
+ indicates a sample video with the filename and tags, the tags are organized
24
+ as different categories. Example of a video dictionary:
25
+
26
+ .. code-block:: txt
27
+
28
+ {
29
+ 'filename': 'gD_G1b0wV5I_001015_001035.mp4',
30
+ 'label': {
31
+ 'concept': [250, 131, 42, 51, 57, 155, 122],
32
+ 'object': [1570, 508],
33
+ 'event': [16],
34
+ 'action': [180],
35
+ 'scene': [206]
36
+ }
37
+ }
38
+
39
+ Example of a rawframe dictionary:
40
+
41
+ .. code-block:: txt
42
+
43
+ {
44
+ 'frame_dir': 'gD_G1b0wV5I_001015_001035',
45
+ 'total_frames': 61
46
+ 'label': {
47
+ 'concept': [250, 131, 42, 51, 57, 155, 122],
48
+ 'object': [1570, 508],
49
+ 'event': [16],
50
+ 'action': [180],
51
+ 'scene': [206]
52
+ }
53
+ }
54
+
55
+
56
+ Args:
57
+ ann_file (str): Path to the annotation file, should be a json file.
58
+ pipeline (list[dict | callable]): A sequence of data transforms.
59
+ tag_categories (list[str]): List of category names of tags.
60
+ tag_category_nums (list[int]): List of number of tags in each category.
61
+ filename_tmpl (str | None): Template for each filename. If set to None,
62
+ video dataset is used. Default: None.
63
+ **kwargs: Keyword arguments for ``BaseDataset``.
64
+ """
65
+
66
+ def __init__(self,
67
+ ann_file,
68
+ pipeline,
69
+ tag_categories,
70
+ tag_category_nums,
71
+ filename_tmpl=None,
72
+ **kwargs):
73
+ assert len(tag_categories) == len(tag_category_nums)
74
+ self.tag_categories = tag_categories
75
+ self.tag_category_nums = tag_category_nums
76
+ self.filename_tmpl = filename_tmpl
77
+ self.num_categories = len(self.tag_categories)
78
+ self.num_tags = sum(self.tag_category_nums)
79
+ self.category2num = dict(zip(tag_categories, tag_category_nums))
80
+ self.start_idx = [0]
81
+ for i in range(self.num_categories - 1):
82
+ self.start_idx.append(self.start_idx[-1] +
83
+ self.tag_category_nums[i])
84
+ self.category2startidx = dict(zip(tag_categories, self.start_idx))
85
+ self.start_index = kwargs.pop('start_index', 0)
86
+ self.dataset_type = None
87
+ super().__init__(
88
+ ann_file, pipeline, start_index=self.start_index, **kwargs)
89
+
90
+ def load_annotations(self):
91
+ """Load annotation file to get video information."""
92
+ assert self.ann_file.endswith('.json')
93
+ return self.load_json_annotations()
94
+
95
+ def load_json_annotations(self):
96
+ video_infos = mmcv.load(self.ann_file)
97
+ num_videos = len(video_infos)
98
+
99
+ video_info0 = video_infos[0]
100
+ assert ('filename' in video_info0) != ('frame_dir' in video_info0)
101
+ path_key = 'filename' if 'filename' in video_info0 else 'frame_dir'
102
+ self.dataset_type = 'video' if path_key == 'filename' else 'rawframe'
103
+ if self.dataset_type == 'rawframe':
104
+ assert self.filename_tmpl is not None
105
+
106
+ for i in range(num_videos):
107
+ path_value = video_infos[i][path_key]
108
+ if self.data_prefix is not None:
109
+ path_value = osp.join(self.data_prefix, path_value)
110
+ video_infos[i][path_key] = path_value
111
+
112
+ # We will convert label to torch tensors in the pipeline
113
+ video_infos[i]['categories'] = self.tag_categories
114
+ video_infos[i]['category_nums'] = self.tag_category_nums
115
+ if self.dataset_type == 'rawframe':
116
+ video_infos[i]['filename_tmpl'] = self.filename_tmpl
117
+ video_infos[i]['start_index'] = self.start_index
118
+ video_infos[i]['modality'] = self.modality
119
+
120
+ return video_infos
121
+
122
+ @staticmethod
123
+ def label2array(num, label):
124
+ arr = np.zeros(num, dtype=np.float32)
125
+ arr[label] = 1.
126
+ return arr
127
+
128
+ def evaluate(self,
129
+ results,
130
+ metrics='mean_average_precision',
131
+ metric_options=None,
132
+ logger=None):
133
+ """Evaluation in HVU Video Dataset. We only support evaluating mAP for
134
+ each tag categories. Since some tag categories are missing for some
135
+ videos, we can not evaluate mAP for all tags.
136
+
137
+ Args:
138
+ results (list): Output results.
139
+ metrics (str | sequence[str]): Metrics to be performed.
140
+ Defaults: 'mean_average_precision'.
141
+ metric_options (dict | None): Dict for metric options.
142
+ Default: None.
143
+ logger (logging.Logger | None): Logger for recording.
144
+ Default: None.
145
+
146
+ Returns:
147
+ dict: Evaluation results dict.
148
+ """
149
+ # Protect ``metric_options`` since it uses mutable value as default
150
+ metric_options = copy.deepcopy(metric_options)
151
+
152
+ if not isinstance(results, list):
153
+ raise TypeError(f'results must be a list, but got {type(results)}')
154
+ assert len(results) == len(self), (
155
+ f'The length of results is not equal to the dataset len: '
156
+ f'{len(results)} != {len(self)}')
157
+
158
+ metrics = metrics if isinstance(metrics, (list, tuple)) else [metrics]
159
+
160
+ # There should be only one metric in the metrics list:
161
+ # 'mean_average_precision'
162
+ assert len(metrics) == 1
163
+ metric = metrics[0]
164
+ assert metric == 'mean_average_precision'
165
+
166
+ gt_labels = [ann['label'] for ann in self.video_infos]
167
+
168
+ eval_results = OrderedDict()
169
+
170
+ for category in self.tag_categories:
171
+
172
+ start_idx = self.category2startidx[category]
173
+ num = self.category2num[category]
174
+ preds = [
175
+ result[start_idx:start_idx + num]
176
+ for video_idx, result in enumerate(results)
177
+ if category in gt_labels[video_idx]
178
+ ]
179
+ gts = [
180
+ gt_label[category] for gt_label in gt_labels
181
+ if category in gt_label
182
+ ]
183
+
184
+ gts = [self.label2array(num, item) for item in gts]
185
+
186
+ mAP = mean_average_precision(preds, gts)
187
+ eval_results[f'{category}_mAP'] = mAP
188
+ log_msg = f'\n{category}_mAP\t{mAP:.4f}'
189
+ print_log(log_msg, logger=logger)
190
+
191
+ return eval_results
TransRAC/mmaction/datasets/image_dataset.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .builder import DATASETS
2
+ from .video_dataset import VideoDataset
3
+
4
+
5
+ @DATASETS.register_module()
6
+ class ImageDataset(VideoDataset):
7
+ """Image dataset for action recognition, used in the Project OmniSource.
8
+
9
+ The dataset loads image list and apply specified transforms to return a
10
+ dict containing the image tensors and other information. For the
11
+ ImageDataset
12
+
13
+ The ann_file is a text file with multiple lines, and each line indicates
14
+ the image path and the image label, which are split with a whitespace.
15
+ Example of a annotation file:
16
+
17
+ .. code-block:: txt
18
+
19
+ path/to/image1.jpg 1
20
+ path/to/image2.jpg 1
21
+ path/to/image3.jpg 2
22
+ path/to/image4.jpg 2
23
+ path/to/image5.jpg 3
24
+ path/to/image6.jpg 3
25
+
26
+ Example of a multi-class annotation file:
27
+
28
+ .. code-block:: txt
29
+
30
+ path/to/image1.jpg 1 3 5
31
+ path/to/image2.jpg 1 2
32
+ path/to/image3.jpg 2
33
+ path/to/image4.jpg 2 4 6 8
34
+ path/to/image5.jpg 3
35
+ path/to/image6.jpg 3
36
+
37
+ Args:
38
+ ann_file (str): Path to the annotation file.
39
+ pipeline (list[dict | callable]): A sequence of data transforms.
40
+ **kwargs: Keyword arguments for ``BaseDataset``.
41
+ """
42
+
43
+ def __init__(self, ann_file, pipeline, **kwargs):
44
+ super().__init__(ann_file, pipeline, start_index=None, **kwargs)
45
+ # use `start_index=None` to indicate it is for `ImageDataset`
TransRAC/mmaction/datasets/pose_dataset.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+
3
+ import mmcv
4
+ import numpy as np
5
+
6
+ from ..utils import get_root_logger
7
+ from .base import BaseDataset
8
+ from .builder import DATASETS
9
+
10
+
11
+ @DATASETS.register_module()
12
+ class PoseDataset(BaseDataset):
13
+ """Pose dataset for action recognition.
14
+
15
+ The dataset loads pose and apply specified transforms to return a
16
+ dict containing pose information.
17
+
18
+ The ann_file is a pickle file, the json file contains a list of
19
+ annotations, the fields of an annotation include frame_dir(video_id),
20
+ total_frames, label, kp, kpscore.
21
+
22
+ Args:
23
+ ann_file (str): Path to the annotation file.
24
+ pipeline (list[dict | callable]): A sequence of data transforms.
25
+ valid_ratio (float | None): The valid_ratio for videos in KineticsPose.
26
+ For a video with n frames, it is a valid training sample only if
27
+ n * valid_ratio frames have human pose. None means not applicable
28
+ (only applicable to Kinetics Pose). Default: None.
29
+ box_thr (str | None): The threshold for human proposals. Only boxes
30
+ with confidence score larger than `box_thr` is kept. None means
31
+ not applicable (only applicable to Kinetics Pose [ours]). Allowed
32
+ choices are '0.5', '0.6', '0.7', '0.8', '0.9'. Default: None.
33
+ class_prob (dict | None): The per class sampling probability. If not
34
+ None, it will override the class_prob calculated in
35
+ BaseDataset.__init__(). Default: None.
36
+ **kwargs: Keyword arguments for ``BaseDataset``.
37
+ """
38
+
39
+ def __init__(self,
40
+ ann_file,
41
+ pipeline,
42
+ valid_ratio=None,
43
+ box_thr=None,
44
+ class_prob=None,
45
+ **kwargs):
46
+ modality = 'Pose'
47
+
48
+ super().__init__(
49
+ ann_file, pipeline, start_index=0, modality=modality, **kwargs)
50
+
51
+ # box_thr, which should be a string
52
+ self.box_thr = box_thr
53
+ if self.box_thr is not None:
54
+ assert box_thr in ['0.5', '0.6', '0.7', '0.8', '0.9']
55
+
56
+ # Thresholding Training Examples
57
+ self.valid_ratio = valid_ratio
58
+ if self.valid_ratio is not None:
59
+ assert isinstance(self.valid_ratio, float)
60
+ if self.box_thr is None:
61
+ self.video_infos = self.video_infos = [
62
+ x for x in self.video_infos
63
+ if x['valid_frames'] / x['total_frames'] >= valid_ratio
64
+ ]
65
+ else:
66
+ key = f'valid@{self.box_thr}'
67
+ self.video_infos = [
68
+ x for x in self.video_infos
69
+ if x[key] / x['total_frames'] >= valid_ratio
70
+ ]
71
+ if self.box_thr != '0.5':
72
+ box_thr = float(self.box_thr)
73
+ for item in self.video_infos:
74
+ inds = [
75
+ i for i, score in enumerate(item['box_score'])
76
+ if score >= box_thr
77
+ ]
78
+ item['anno_inds'] = np.array(inds)
79
+
80
+ if class_prob is not None:
81
+ self.class_prob = class_prob
82
+
83
+ logger = get_root_logger()
84
+ logger.info(f'{len(self)} videos remain after valid thresholding')
85
+
86
+ def load_annotations(self):
87
+ """Load annotation file to get video information."""
88
+ assert self.ann_file.endswith('.pkl')
89
+ return self.load_pkl_annotations()
90
+
91
+ def load_pkl_annotations(self):
92
+ data = mmcv.load(self.ann_file)
93
+
94
+ for item in data:
95
+ # Sometimes we may need to load anno from the file
96
+ if 'filename' in item:
97
+ item['filename'] = osp.join(self.data_prefix, item['filename'])
98
+ return data
TransRAC/mmaction/datasets/rawframe_dataset.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os.path as osp
3
+
4
+ import torch
5
+
6
+ from .base import BaseDataset
7
+ from .builder import DATASETS
8
+
9
+
10
+ @DATASETS.register_module()
11
+ class RawframeDataset(BaseDataset):
12
+ """Rawframe dataset for action recognition.
13
+
14
+ The dataset loads raw frames and apply specified transforms to return a
15
+ dict containing the frame tensors and other information.
16
+
17
+ The ann_file is a text file with multiple lines, and each line indicates
18
+ the directory to frames of a video, total frames of the video and
19
+ the label of a video, which are split with a whitespace.
20
+ Example of a annotation file:
21
+
22
+ .. code-block:: txt
23
+
24
+ some/directory-1 163 1
25
+ some/directory-2 122 1
26
+ some/directory-3 258 2
27
+ some/directory-4 234 2
28
+ some/directory-5 295 3
29
+ some/directory-6 121 3
30
+
31
+ Example of a multi-class annotation file:
32
+
33
+
34
+ .. code-block:: txt
35
+
36
+ some/directory-1 163 1 3 5
37
+ some/directory-2 122 1 2
38
+ some/directory-3 258 2
39
+ some/directory-4 234 2 4 6 8
40
+ some/directory-5 295 3
41
+ some/directory-6 121 3
42
+
43
+ Example of a with_offset annotation file (clips from long videos), each
44
+ line indicates the directory to frames of a video, the index of the start
45
+ frame, total frames of the video clip and the label of a video clip, which
46
+ are split with a whitespace.
47
+
48
+
49
+ .. code-block:: txt
50
+
51
+ some/directory-1 12 163 3
52
+ some/directory-2 213 122 4
53
+ some/directory-3 100 258 5
54
+ some/directory-4 98 234 2
55
+ some/directory-5 0 295 3
56
+ some/directory-6 50 121 3
57
+
58
+
59
+ Args:
60
+ ann_file (str): Path to the annotation file.
61
+ pipeline (list[dict | callable]): A sequence of data transforms.
62
+ data_prefix (str | None): Path to a directory where videos are held.
63
+ Default: None.
64
+ test_mode (bool): Store True when building test or validation dataset.
65
+ Default: False.
66
+ filename_tmpl (str): Template for each filename.
67
+ Default: 'img_{:05}.jpg'.
68
+ with_offset (bool): Determines whether the offset information is in
69
+ ann_file. Default: False.
70
+ multi_class (bool): Determines whether it is a multi-class
71
+ recognition dataset. Default: False.
72
+ num_classes (int | None): Number of classes in the dataset.
73
+ Default: None.
74
+ modality (str): Modality of data. Support 'RGB', 'Flow'.
75
+ Default: 'RGB'.
76
+ sample_by_class (bool): Sampling by class, should be set `True` when
77
+ performing inter-class data balancing. Only compatible with
78
+ `multi_class == False`. Only applies for training. Default: False.
79
+ power (float): We support sampling data with the probability
80
+ proportional to the power of its label frequency (freq ^ power)
81
+ when sampling data. `power == 1` indicates uniformly sampling all
82
+ data; `power == 0` indicates uniformly sampling all classes.
83
+ Default: 0.
84
+ dynamic_length (bool): If the dataset length is dynamic (used by
85
+ ClassSpecificDistributedSampler). Default: False.
86
+ """
87
+
88
+ def __init__(self,
89
+ ann_file,
90
+ pipeline,
91
+ data_prefix=None,
92
+ test_mode=False,
93
+ filename_tmpl='img_{:05}.jpg',
94
+ with_offset=False,
95
+ multi_class=False,
96
+ num_classes=None,
97
+ start_index=1,
98
+ modality='RGB',
99
+ sample_by_class=False,
100
+ power=0.,
101
+ dynamic_length=False):
102
+ self.filename_tmpl = filename_tmpl
103
+ self.with_offset = with_offset
104
+ super().__init__(
105
+ ann_file,
106
+ pipeline,
107
+ data_prefix,
108
+ test_mode,
109
+ multi_class,
110
+ num_classes,
111
+ start_index,
112
+ modality,
113
+ sample_by_class=sample_by_class,
114
+ power=power,
115
+ dynamic_length=dynamic_length)
116
+
117
+ def load_annotations(self):
118
+ """Load annotation file to get video information."""
119
+ if self.ann_file.endswith('.json'):
120
+ return self.load_json_annotations()
121
+ video_infos = []
122
+ with open(self.ann_file, 'r') as fin:
123
+ for line in fin:
124
+ line_split = line.strip().split()
125
+ video_info = {}
126
+ idx = 0
127
+ # idx for frame_dir
128
+ frame_dir = line_split[idx]
129
+ if self.data_prefix is not None:
130
+ frame_dir = osp.join(self.data_prefix, frame_dir)
131
+ video_info['frame_dir'] = frame_dir
132
+ idx += 1
133
+ if self.with_offset:
134
+ # idx for offset and total_frames
135
+ video_info['offset'] = int(line_split[idx])
136
+ video_info['total_frames'] = int(line_split[idx + 1])
137
+ idx += 2
138
+ else:
139
+ # idx for total_frames
140
+ video_info['total_frames'] = int(line_split[idx])
141
+ idx += 1
142
+ # idx for label[s]
143
+ label = [int(x) for x in line_split[idx:]]
144
+ assert label, f'missing label in line: {line}'
145
+ if self.multi_class:
146
+ assert self.num_classes is not None
147
+ video_info['label'] = label
148
+ else:
149
+ assert len(label) == 1
150
+ video_info['label'] = label[0]
151
+ video_infos.append(video_info)
152
+
153
+ return video_infos
154
+
155
+ def prepare_train_frames(self, idx):
156
+ """Prepare the frames for training given the index."""
157
+ results = copy.deepcopy(self.video_infos[idx])
158
+ results['filename_tmpl'] = self.filename_tmpl
159
+ results['modality'] = self.modality
160
+ results['start_index'] = self.start_index
161
+
162
+ # prepare tensor in getitem
163
+ if self.multi_class:
164
+ onehot = torch.zeros(self.num_classes)
165
+ onehot[results['label']] = 1.
166
+ results['label'] = onehot
167
+
168
+ return self.pipeline(results)
169
+
170
+ def prepare_test_frames(self, idx):
171
+ """Prepare the frames for testing given the index."""
172
+ results = copy.deepcopy(self.video_infos[idx])
173
+ results['filename_tmpl'] = self.filename_tmpl
174
+ results['modality'] = self.modality
175
+ results['start_index'] = self.start_index
176
+
177
+ # prepare tensor in getitem
178
+ if self.multi_class:
179
+ onehot = torch.zeros(self.num_classes)
180
+ onehot[results['label']] = 1.
181
+ results['label'] = onehot
182
+
183
+ return self.pipeline(results)
TransRAC/mmaction/datasets/rawvideo_dataset.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os.path as osp
3
+ import random
4
+
5
+ import mmcv
6
+
7
+ from .base import BaseDataset
8
+ from .builder import DATASETS
9
+
10
+
11
+ @DATASETS.register_module()
12
+ class RawVideoDataset(BaseDataset):
13
+ """RawVideo dataset for action recognition, used in the Project OmniSource.
14
+
15
+ The dataset loads clips of raw videos and apply specified transforms to
16
+ return a dict containing the frame tensors and other information. Not that
17
+ for this dataset, `multi_class` should be False.
18
+
19
+ The ann_file is a text file with multiple lines, and each line indicates
20
+ a sample video with the filepath (without suffix), label, number of clips
21
+ and index of positive clips (starting from 0), which are split with a
22
+ whitespace. Raw videos should be first trimmed into 10 second clips,
23
+ organized in the following format:
24
+
25
+ .. code-block:: txt
26
+
27
+ some/path/D32_1gwq35E/part_0.mp4
28
+ some/path/D32_1gwq35E/part_1.mp4
29
+ ......
30
+ some/path/D32_1gwq35E/part_n.mp4
31
+
32
+ Example of a annotation file:
33
+
34
+ .. code-block:: txt
35
+
36
+ some/path/D32_1gwq35E 66 10 0 1 2
37
+ some/path/-G-5CJ0JkKY 254 5 3 4
38
+ some/path/T4h1bvOd9DA 33 1 0
39
+ some/path/4uZ27ivBl00 341 2 0 1
40
+ some/path/0LfESFkfBSw 186 234 7 9 11
41
+ some/path/-YIsNpBEx6c 169 100 9 10 11
42
+
43
+ The first line indicates that the raw video `some/path/D32_1gwq35E` has
44
+ action label `66`, consists of 10 clips (from `part_0.mp4` to
45
+ `part_9.mp4`). The 1st, 2nd and 3rd clips are positive clips.
46
+
47
+
48
+ Args:
49
+ ann_file (str): Path to the annotation file.
50
+ pipeline (list[dict | callable]): A sequence of data transforms.
51
+ sampling_strategy (str): The strategy to sample clips from raw videos.
52
+ Choices are 'random' or 'positive'. Default: 'positive'.
53
+ clipname_tmpl (str): The template of clip name in the raw video.
54
+ Default: 'part_{}.mp4'.
55
+ **kwargs: Keyword arguments for ``BaseDataset``.
56
+ """
57
+
58
+ def __init__(self,
59
+ ann_file,
60
+ pipeline,
61
+ clipname_tmpl='part_{}.mp4',
62
+ sampling_strategy='positive',
63
+ **kwargs):
64
+ super().__init__(ann_file, pipeline, start_index=0, **kwargs)
65
+ assert self.multi_class is False
66
+ self.sampling_strategy = sampling_strategy
67
+ self.clipname_tmpl = clipname_tmpl
68
+ # If positive, we should only keep those raw videos with positive
69
+ # clips
70
+ if self.sampling_strategy == 'positive':
71
+ self.video_infos = [
72
+ x for x in self.video_infos if len(x['positive_clip_inds'])
73
+ ]
74
+
75
+ # do not support multi_class
76
+ def load_annotations(self):
77
+ """Load annotation file to get video information."""
78
+ if self.ann_file.endswith('.json'):
79
+ return self.load_json_annotations()
80
+
81
+ video_infos = []
82
+ with open(self.ann_file, 'r') as fin:
83
+ for line in fin:
84
+ line_split = line.strip().split()
85
+ video_dir = line_split[0]
86
+ label = int(line_split[1])
87
+ num_clips = int(line_split[2])
88
+ positive_clip_inds = [int(ind) for ind in line_split[3:]]
89
+
90
+ if self.data_prefix is not None:
91
+ video_dir = osp.join(self.data_prefix, video_dir)
92
+ video_infos.append(
93
+ dict(
94
+ video_dir=video_dir,
95
+ label=label,
96
+ num_clips=num_clips,
97
+ positive_clip_inds=positive_clip_inds))
98
+ return video_infos
99
+
100
+ # do not support multi_class
101
+ def load_json_annotations(self):
102
+ """Load json annotation file to get video information."""
103
+ video_infos = mmcv.load(self.ann_file)
104
+ num_videos = len(video_infos)
105
+ path_key = 'video_dir'
106
+ for i in range(num_videos):
107
+ if self.data_prefix is not None:
108
+ path_value = video_infos[i][path_key]
109
+ path_value = osp.join(self.data_prefix, path_value)
110
+ video_infos[i][path_key] = path_value
111
+ return video_infos
112
+
113
+ def sample_clip(self, results):
114
+ """Sample a clip from the raw video given the sampling strategy."""
115
+ assert self.sampling_strategy in ['positive', 'random']
116
+ if self.sampling_strategy == 'positive':
117
+ assert results['positive_clip_inds']
118
+ ind = random.choice(results['positive_clip_inds'])
119
+ else:
120
+ ind = random.randint(0, results['num_clips'] - 1)
121
+ clipname = self.clipname_tmpl.format(ind)
122
+
123
+ # if the first char of self.clipname_tmpl is a letter, use osp.join;
124
+ # otherwise, directly concat them
125
+ if self.clipname_tmpl[0].isalpha():
126
+ filename = osp.join(results['video_dir'], clipname)
127
+ else:
128
+ filename = results['video_dir'] + clipname
129
+ results['filename'] = filename
130
+ return results
131
+
132
+ def prepare_train_frames(self, idx):
133
+ """Prepare the frames for training given the index."""
134
+ results = copy.deepcopy(self.video_infos[idx])
135
+ results = self.sample_clip(results)
136
+ results['modality'] = self.modality
137
+ results['start_index'] = self.start_index
138
+ return self.pipeline(results)
139
+
140
+ def prepare_test_frames(self, idx):
141
+ """Prepare the frames for testing given the index."""
142
+ results = copy.deepcopy(self.video_infos[idx])
143
+ results = self.sample_clip(results)
144
+ results['modality'] = self.modality
145
+ results['start_index'] = self.start_index
146
+ return self.pipeline(results)
TransRAC/mmaction/datasets/ssn_dataset.py ADDED
@@ -0,0 +1,881 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import copy
2
+ import os.path as osp
3
+ import warnings
4
+ from collections import OrderedDict
5
+
6
+ import mmcv
7
+ import numpy as np
8
+ from torch.nn.modules.utils import _pair
9
+
10
+ from ..core import softmax
11
+ from ..localization import (eval_ap, load_localize_proposal_file,
12
+ perform_regression, temporal_iou, temporal_nms)
13
+ from ..utils import get_root_logger
14
+ from .base import BaseDataset
15
+ from .builder import DATASETS
16
+
17
+
18
+ class SSNInstance:
19
+ """Proposal instance of SSN.
20
+
21
+ Args:
22
+ start_frame (int): Index of the proposal's start frame.
23
+ end_frame (int): Index of the proposal's end frame.
24
+ num_video_frames (int): Total frames of the video.
25
+ label (int | None): The category label of the proposal. Default: None.
26
+ best_iou (float): The highest IOU with the groundtruth instance.
27
+ Default: 0.
28
+ overlap_self (float): Percent of the proposal's own span contained
29
+ in a groundtruth instance. Default: 0.
30
+ """
31
+
32
+ def __init__(self,
33
+ start_frame,
34
+ end_frame,
35
+ num_video_frames,
36
+ label=None,
37
+ best_iou=0,
38
+ overlap_self=0):
39
+ self.start_frame = start_frame
40
+ self.end_frame = min(end_frame, num_video_frames)
41
+ self.num_video_frames = num_video_frames
42
+ self.label = label if label is not None else -1
43
+ self.coverage = (end_frame - start_frame) / num_video_frames
44
+ self.best_iou = best_iou
45
+ self.overlap_self = overlap_self
46
+ self.loc_reg = None
47
+ self.size_reg = None
48
+ self.regression_targets = [0., 0.]
49
+
50
+ def compute_regression_targets(self, gt_list):
51
+ """Compute regression targets of positive proposals.
52
+
53
+ Args:
54
+ gt_list (list): The list of groundtruth instances.
55
+ """
56
+ # Find the groundtruth instance with the highest IOU.
57
+ ious = [
58
+ temporal_iou(self.start_frame, self.end_frame, gt.start_frame,
59
+ gt.end_frame) for gt in gt_list
60
+ ]
61
+ best_gt = gt_list[np.argmax(ious)]
62
+
63
+ # interval: [start_frame, end_frame)
64
+ proposal_center = (self.start_frame + self.end_frame - 1) / 2
65
+ gt_center = (best_gt.start_frame + best_gt.end_frame - 1) / 2
66
+ proposal_size = self.end_frame - self.start_frame
67
+ gt_size = best_gt.end_frame - best_gt.start_frame
68
+
69
+ # Get regression targets:
70
+ # (1). Localization regression target:
71
+ # center shift proportional to the proposal duration
72
+ # (2). Duration/Size regression target:
73
+ # logarithm of the groundtruth duration over proposal duration
74
+
75
+ self.loc_reg = (gt_center - proposal_center) / proposal_size
76
+ self.size_reg = np.log(gt_size / proposal_size)
77
+ self.regression_targets = ([self.loc_reg, self.size_reg]
78
+ if self.loc_reg is not None else [0., 0.])
79
+
80
+
81
+ @DATASETS.register_module()
82
+ class SSNDataset(BaseDataset):
83
+ """Proposal frame dataset for Structured Segment Networks.
84
+
85
+ Based on proposal information, the dataset loads raw frames and applies
86
+ specified transforms to return a dict containing the frame tensors and
87
+ other information.
88
+
89
+ The ann_file is a text file with multiple lines and each
90
+ video's information takes up several lines. This file can be a normalized
91
+ file with percent or standard file with specific frame indexes. If the file
92
+ is a normalized file, it will be converted into a standard file first.
93
+
94
+ Template information of a video in a standard file:
95
+ .. code-block:: txt
96
+ # index
97
+ video_id
98
+ num_frames
99
+ fps
100
+ num_gts
101
+ label, start_frame, end_frame
102
+ label, start_frame, end_frame
103
+ ...
104
+ num_proposals
105
+ label, best_iou, overlap_self, start_frame, end_frame
106
+ label, best_iou, overlap_self, start_frame, end_frame
107
+ ...
108
+
109
+ Example of a standard annotation file:
110
+ .. code-block:: txt
111
+ # 0
112
+ video_validation_0000202
113
+ 5666
114
+ 1
115
+ 3
116
+ 8 130 185
117
+ 8 832 1136
118
+ 8 1303 1381
119
+ 5
120
+ 8 0.0620 0.0620 790 5671
121
+ 8 0.1656 0.1656 790 2619
122
+ 8 0.0833 0.0833 3945 5671
123
+ 8 0.0960 0.0960 4173 5671
124
+ 8 0.0614 0.0614 3327 5671
125
+
126
+ Args:
127
+ ann_file (str): Path to the annotation file.
128
+ pipeline (list[dict | callable]): A sequence of data transforms.
129
+ train_cfg (dict): Config for training.
130
+ test_cfg (dict): Config for testing.
131
+ data_prefix (str): Path to a directory where videos are held.
132
+ test_mode (bool): Store True when building test or validation dataset.
133
+ Default: False.
134
+ filename_tmpl (str): Template for each filename.
135
+ Default: 'img_{:05}.jpg'.
136
+ start_index (int): Specify a start index for frames in consideration of
137
+ different filename format. Default: 1.
138
+ modality (str): Modality of data. Support 'RGB', 'Flow'.
139
+ Default: 'RGB'.
140
+ video_centric (bool): Whether to sample proposals just from
141
+ this video or sample proposals randomly from the entire dataset.
142
+ Default: True.
143
+ reg_normalize_constants (list): Regression target normalized constants,
144
+ including mean and standard deviation of location and duration.
145
+ body_segments (int): Number of segments in course period.
146
+ Default: 5.
147
+ aug_segments (list[int]): Number of segments in starting and
148
+ ending period. Default: (2, 2).
149
+ aug_ratio (int | float | tuple[int | float]): The ratio of the length
150
+ of augmentation to that of the proposal. Defualt: (0.5, 0.5).
151
+ clip_len (int): Frames of each sampled output clip.
152
+ Default: 1.
153
+ frame_interval (int): Temporal interval of adjacent sampled frames.
154
+ Default: 1.
155
+ filter_gt (bool): Whether to filter videos with no annotation
156
+ during training. Default: True.
157
+ use_regression (bool): Whether to perform regression. Default: True.
158
+ verbose (bool): Whether to print full information or not.
159
+ Default: False.
160
+ """
161
+
162
+ def __init__(self,
163
+ ann_file,
164
+ pipeline,
165
+ train_cfg,
166
+ test_cfg,
167
+ data_prefix,
168
+ test_mode=False,
169
+ filename_tmpl='img_{:05d}.jpg',
170
+ start_index=1,
171
+ modality='RGB',
172
+ video_centric=True,
173
+ reg_normalize_constants=None,
174
+ body_segments=5,
175
+ aug_segments=(2, 2),
176
+ aug_ratio=(0.5, 0.5),
177
+ clip_len=1,
178
+ frame_interval=1,
179
+ filter_gt=True,
180
+ use_regression=True,
181
+ verbose=False):
182
+ self.logger = get_root_logger()
183
+ super().__init__(
184
+ ann_file,
185
+ pipeline,
186
+ data_prefix=data_prefix,
187
+ test_mode=test_mode,
188
+ start_index=start_index,
189
+ modality=modality)
190
+ self.train_cfg = train_cfg
191
+ self.test_cfg = test_cfg
192
+ self.assigner = train_cfg.ssn.assigner
193
+ self.sampler = train_cfg.ssn.sampler
194
+ self.evaluater = test_cfg.ssn.evaluater
195
+ self.verbose = verbose
196
+ self.filename_tmpl = filename_tmpl
197
+
198
+ if filter_gt or not test_mode:
199
+ valid_inds = [
200
+ i for i, video_info in enumerate(self.video_infos)
201
+ if len(video_info['gts']) > 0
202
+ ]
203
+ self.logger.info(f'{len(valid_inds)} out of {len(self.video_infos)} '
204
+ f'videos are valid.')
205
+ self.video_infos = [self.video_infos[i] for i in valid_inds]
206
+
207
+ # construct three pools:
208
+ # 1. Positive(Foreground)
209
+ # 2. Background
210
+ # 3. Incomplete
211
+ self.positive_pool = []
212
+ self.background_pool = []
213
+ self.incomplete_pool = []
214
+ self.construct_proposal_pools()
215
+
216
+ if reg_normalize_constants is None:
217
+ self.reg_norm_consts = self._compute_reg_normalize_constants()
218
+ else:
219
+ self.reg_norm_consts = reg_normalize_constants
220
+ self.video_centric = video_centric
221
+ self.body_segments = body_segments
222
+ self.aug_segments = aug_segments
223
+ self.aug_ratio = _pair(aug_ratio)
224
+ if not mmcv.is_tuple_of(self.aug_ratio, (int, float)):
225
+ raise TypeError(f'aug_ratio should be int, float'
226
+ f'or tuple of int and float, '
227
+ f'but got {type(aug_ratio)}')
228
+ assert len(self.aug_ratio) == 2
229
+
230
+ total_ratio = (
231
+ self.sampler.positive_ratio + self.sampler.background_ratio +
232
+ self.sampler.incomplete_ratio)
233
+ self.positive_per_video = int(
234
+ self.sampler.num_per_video *
235
+ (self.sampler.positive_ratio / total_ratio))
236
+ self.background_per_video = int(
237
+ self.sampler.num_per_video *
238
+ (self.sampler.background_ratio / total_ratio))
239
+ self.incomplete_per_video = (
240
+ self.sampler.num_per_video - self.positive_per_video -
241
+ self.background_per_video)
242
+
243
+ self.test_interval = self.test_cfg.ssn.sampler.test_interval
244
+ # number of consecutive frames
245
+ self.clip_len = clip_len
246
+ # number of steps (sparse sampling for efficiency of io)
247
+ self.frame_interval = frame_interval
248
+
249
+ # test mode or not
250
+ self.filter_gt = filter_gt
251
+ self.use_regression = use_regression
252
+ self.test_mode = test_mode
253
+
254
+ # yapf: disable
255
+ if self.verbose:
256
+ self.logger.info(f"""
257
+ SSNDataset: proposal file {self.proposal_file} parsed.
258
+
259
+ There are {len(self.positive_pool) + len(self.background_pool) +
260
+ len(self.incomplete_pool)} usable proposals from {len(self.video_infos)} videos.
261
+ {len(self.positive_pool)} positive proposals
262
+ {len(self.incomplete_pool)} incomplete proposals
263
+ {len(self.background_pool)} background proposals
264
+
265
+ Sample config:
266
+ FG/BG/INCOMP: {self.positive_per_video}/{self.background_per_video}/{self.incomplete_per_video} # noqa:E501
267
+ Video Centric: {self.video_centric}
268
+
269
+ Regression Normalization Constants:
270
+ Location: mean {self.reg_norm_consts[0][0]:.05f} std {self.reg_norm_consts[1][0]:.05f} # noqa: E501
271
+ Duration: mean {self.reg_norm_consts[0][1]:.05f} std {self.reg_norm_consts[1][1]:.05f} # noqa: E501
272
+ """)
273
+ # yapf: enable
274
+ else:
275
+ self.logger.info(
276
+ f'SSNDataset: proposal file {self.proposal_file} parsed.')
277
+
278
+ def load_annotations(self):
279
+ """Load annotation file to get video information."""
280
+ video_infos = []
281
+ if 'normalized_' in self.ann_file:
282
+ self.proposal_file = self.ann_file.replace('normalized_', '')
283
+ if not osp.exists(self.proposal_file):
284
+ raise Exception(f'Please refer to `$MMACTION2/tools/data` to'
285
+ f'denormalize {self.ann_file}.')
286
+ else:
287
+ self.proposal_file = self.ann_file
288
+ proposal_infos = load_localize_proposal_file(self.proposal_file)
289
+ # proposal_info:[video_id, num_frames, gt_list, proposal_list]
290
+ # gt_list member: [label, start_frame, end_frame]
291
+ # proposal_list member: [label, best_iou, overlap_self,
292
+ # start_frame, end_frame]
293
+ for proposal_info in proposal_infos:
294
+ if self.data_prefix is not None:
295
+ frame_dir = osp.join(self.data_prefix, proposal_info[0])
296
+ num_frames = int(proposal_info[1])
297
+ # gts:start, end, num_frames, class_label, tIoU=1
298
+ gts = []
299
+ for x in proposal_info[2]:
300
+ if int(x[2]) > int(x[1]) and int(x[1]) < num_frames:
301
+ ssn_instance = SSNInstance(
302
+ int(x[1]),
303
+ int(x[2]),
304
+ num_frames,
305
+ label=int(x[0]),
306
+ best_iou=1.0)
307
+ gts.append(ssn_instance)
308
+ # proposals:start, end, num_frames, class_label
309
+ # tIoU=best_iou, overlap_self
310
+ proposals = []
311
+ for x in proposal_info[3]:
312
+ if int(x[4]) > int(x[3]) and int(x[3]) < num_frames:
313
+ ssn_instance = SSNInstance(
314
+ int(x[3]),
315
+ int(x[4]),
316
+ num_frames,
317
+ label=int(x[0]),
318
+ best_iou=float(x[1]),
319
+ overlap_self=float(x[2]))
320
+ proposals.append(ssn_instance)
321
+ video_infos.append(
322
+ dict(
323
+ frame_dir=frame_dir,
324
+ video_id=proposal_info[0],
325
+ total_frames=num_frames,
326
+ gts=gts,
327
+ proposals=proposals))
328
+ return video_infos
329
+
330
+ def results_to_detections(self, results, top_k=2000, **kwargs):
331
+ """Convert prediction results into detections.
332
+
333
+ Args:
334
+ results (list): Prediction results.
335
+ top_k (int): Number of top results. Default: 2000.
336
+
337
+ Returns:
338
+ list: Detection results.
339
+ """
340
+ num_classes = results[0]['activity_scores'].shape[1] - 1
341
+ detections = [dict() for _ in range(num_classes)]
342
+
343
+ for idx in range(len(self)):
344
+ video_id = self.video_infos[idx]['video_id']
345
+ relative_proposals = results[idx]['relative_proposal_list']
346
+ if len(relative_proposals[0].shape) == 3:
347
+ relative_proposals = np.squeeze(relative_proposals, 0)
348
+
349
+ activity_scores = results[idx]['activity_scores']
350
+ completeness_scores = results[idx]['completeness_scores']
351
+ regression_scores = results[idx]['bbox_preds']
352
+ if regression_scores is None:
353
+ regression_scores = np.zeros(
354
+ (len(relative_proposals), num_classes, 2),
355
+ dtype=np.float32)
356
+ regression_scores = regression_scores.reshape((-1, num_classes, 2))
357
+
358
+ if top_k <= 0:
359
+ combined_scores = (
360
+ softmax(activity_scores[:, 1:], dim=1) *
361
+ np.exp(completeness_scores))
362
+ for i in range(num_classes):
363
+ center_scores = regression_scores[:, i, 0][:, None]
364
+ duration_scores = regression_scores[:, i, 1][:, None]
365
+ detections[i][video_id] = np.concatenate(
366
+ (relative_proposals, combined_scores[:, i][:, None],
367
+ center_scores, duration_scores),
368
+ axis=1)
369
+ else:
370
+ combined_scores = (
371
+ softmax(activity_scores[:, 1:], dim=1) *
372
+ np.exp(completeness_scores))
373
+ keep_idx = np.argsort(combined_scores.ravel())[-top_k:]
374
+ for k in keep_idx:
375
+ class_idx = k % num_classes
376
+ proposal_idx = k // num_classes
377
+ new_item = [
378
+ relative_proposals[proposal_idx, 0],
379
+ relative_proposals[proposal_idx,
380
+ 1], combined_scores[proposal_idx,
381
+ class_idx],
382
+ regression_scores[proposal_idx, class_idx,
383
+ 0], regression_scores[proposal_idx,
384
+ class_idx, 1]
385
+ ]
386
+ if video_id not in detections[class_idx]:
387
+ detections[class_idx][video_id] = np.array([new_item])
388
+ else:
389
+ detections[class_idx][video_id] = np.vstack(
390
+ [detections[class_idx][video_id], new_item])
391
+
392
+ return detections
393
+
394
+ def evaluate(self,
395
+ results,
396
+ metrics='mAP',
397
+ metric_options=dict(mAP=dict(eval_dataset='thumos14')),
398
+ logger=None,
399
+ **deprecated_kwargs):
400
+ """Evaluation in SSN proposal dataset.
401
+
402
+ Args:
403
+ results (list[dict]): Output results.
404
+ metrics (str | sequence[str]): Metrics to be performed.
405
+ Defaults: 'mAP'.
406
+ metric_options (dict): Dict for metric options. Options are
407
+ ``eval_dataset`` for ``mAP``.
408
+ Default: ``dict(mAP=dict(eval_dataset='thumos14'))``.
409
+ logger (logging.Logger | None): Logger for recording.
410
+ Default: None.
411
+ deprecated_kwargs (dict): Used for containing deprecated arguments.
412
+ See 'https://github.com/open-mmlab/mmaction2/pull/286'.
413
+
414
+ Returns:
415
+ dict: Evaluation results for evaluation metrics.
416
+ """
417
+ # Protect ``metric_options`` since it uses mutable value as default
418
+ metric_options = copy.deepcopy(metric_options)
419
+
420
+ if deprecated_kwargs != {}:
421
+ warnings.warn(
422
+ 'Option arguments for metrics has been changed to '
423
+ "`metric_options`, See 'https://github.com/open-mmlab/mmaction2/pull/286' " # noqa: E501
424
+ 'for more details')
425
+ metric_options['mAP'] = dict(metric_options['mAP'],
426
+ **deprecated_kwargs)
427
+
428
+ if not isinstance(results, list):
429
+ raise TypeError(f'results must be a list, but got {type(results)}')
430
+ assert len(results) == len(self), (
431
+ f'The length of results is not equal to the dataset len: '
432
+ f'{len(results)} != {len(self)}')
433
+
434
+ metrics = metrics if isinstance(metrics, (list, tuple)) else [metrics]
435
+ allowed_metrics = ['mAP']
436
+ for metric in metrics:
437
+ if metric not in allowed_metrics:
438
+ raise KeyError(f'metric {metric} is not supported')
439
+
440
+ detections = self.results_to_detections(results, **self.evaluater)
441
+
442
+ if self.use_regression:
443
+ self.logger.info('Performing location regression')
444
+ for class_idx, _ in enumerate(detections):
445
+ detections[class_idx] = {
446
+ k: perform_regression(v)
447
+ for k, v in detections[class_idx].items()
448
+ }
449
+ self.logger.info('Regression finished')
450
+
451
+ self.logger.info('Performing NMS')
452
+ for class_idx, _ in enumerate(detections):
453
+ detections[class_idx] = {
454
+ k: temporal_nms(v, self.evaluater.nms)
455
+ for k, v in detections[class_idx].items()
456
+ }
457
+ self.logger.info('NMS finished')
458
+
459
+ # get gts
460
+ all_gts = self.get_all_gts()
461
+ for class_idx, _ in enumerate(detections):
462
+ if class_idx not in all_gts:
463
+ all_gts[class_idx] = dict()
464
+
465
+ # get predictions
466
+ plain_detections = {}
467
+ for class_idx, _ in enumerate(detections):
468
+ detection_list = []
469
+ for video, dets in detections[class_idx].items():
470
+ detection_list.extend([[video, class_idx] + x[:3]
471
+ for x in dets.tolist()])
472
+ plain_detections[class_idx] = detection_list
473
+
474
+ eval_results = OrderedDict()
475
+ for metric in metrics:
476
+ if metric == 'mAP':
477
+ eval_dataset = metric_options.setdefault('mAP', {}).setdefault(
478
+ 'eval_dataset', 'thumos14')
479
+ if eval_dataset == 'thumos14':
480
+ iou_range = np.arange(0.1, 1.0, .1)
481
+ ap_values = eval_ap(plain_detections, all_gts, iou_range)
482
+ map_ious = ap_values.mean(axis=0)
483
+ self.logger.info('Evaluation finished')
484
+
485
+ for iou, map_iou in zip(iou_range, map_ious):
486
+ eval_results[f'mAP@{iou:.02f}'] = map_iou
487
+
488
+ return eval_results
489
+
490
+ def construct_proposal_pools(self):
491
+ """Construct positve proposal pool, incomplete proposal pool and
492
+ background proposal pool of the entire dataset."""
493
+ for video_info in self.video_infos:
494
+ positives = self.get_positives(
495
+ video_info['gts'], video_info['proposals'],
496
+ self.assigner.positive_iou_threshold,
497
+ self.sampler.add_gt_as_proposals)
498
+ self.positive_pool.extend([(video_info['video_id'], proposal)
499
+ for proposal in positives])
500
+
501
+ incompletes, backgrounds = self.get_negatives(
502
+ video_info['proposals'],
503
+ self.assigner.incomplete_iou_threshold,
504
+ self.assigner.background_iou_threshold,
505
+ self.assigner.background_coverage_threshold,
506
+ self.assigner.incomplete_overlap_threshold)
507
+ self.incomplete_pool.extend([(video_info['video_id'], proposal)
508
+ for proposal in incompletes])
509
+ self.background_pool.extend([video_info['video_id'], proposal]
510
+ for proposal in backgrounds)
511
+
512
+ def get_all_gts(self):
513
+ """Fetch groundtruth instances of the entire dataset."""
514
+ gts = {}
515
+ for video_info in self.video_infos:
516
+ video = video_info['video_id']
517
+ for gt in video_info['gts']:
518
+ class_idx = gt.label - 1
519
+ # gt_info: [relative_start, relative_end]
520
+ gt_info = [
521
+ gt.start_frame / video_info['total_frames'],
522
+ gt.end_frame / video_info['total_frames']
523
+ ]
524
+ gts.setdefault(class_idx, {}).setdefault(video,
525
+ []).append(gt_info)
526
+
527
+ return gts
528
+
529
+ @staticmethod
530
+ def get_positives(gts, proposals, positive_threshold, with_gt=True):
531
+ """Get positive/foreground proposals.
532
+
533
+ Args:
534
+ gts (list): List of groundtruth instances(:obj:`SSNInstance`).
535
+ proposals (list): List of proposal instances(:obj:`SSNInstance`).
536
+ positive_threshold (float): Minimum threshold of overlap of
537
+ positive/foreground proposals and groundtruths.
538
+ with_gt (bool): Whether to include groundtruth instances in
539
+ positive proposals. Default: True.
540
+
541
+ Returns:
542
+ list[:obj:`SSNInstance`]: (positives), positives is a list
543
+ comprised of positive proposal instances.
544
+ """
545
+ positives = [
546
+ proposal for proposal in proposals
547
+ if proposal.best_iou > positive_threshold
548
+ ]
549
+
550
+ if with_gt:
551
+ positives.extend(gts)
552
+
553
+ for proposal in positives:
554
+ proposal.compute_regression_targets(gts)
555
+
556
+ return positives
557
+
558
+ @staticmethod
559
+ def get_negatives(proposals,
560
+ incomplete_iou_threshold,
561
+ background_iou_threshold,
562
+ background_coverage_threshold=0.01,
563
+ incomplete_overlap_threshold=0.7):
564
+ """Get negative proposals, including incomplete proposals and
565
+ background proposals.
566
+
567
+ Args:
568
+ proposals (list): List of proposal instances(:obj:`SSNInstance`).
569
+ incomplete_iou_threshold (float): Maximum threshold of overlap
570
+ of incomplete proposals and groundtruths.
571
+ background_iou_threshold (float): Maximum threshold of overlap
572
+ of background proposals and groundtruths.
573
+ background_coverage_threshold (float): Minimum coverage
574
+ of background proposals in video duration. Default: 0.01.
575
+ incomplete_overlap_threshold (float): Minimum percent of incomplete
576
+ proposals' own span contained in a groundtruth instance.
577
+ Default: 0.7.
578
+
579
+ Returns:
580
+ list[:obj:`SSNInstance`]: (incompletes, backgrounds), incompletes
581
+ and backgrounds are lists comprised of incomplete
582
+ proposal instances and background proposal instances.
583
+ """
584
+ incompletes = []
585
+ backgrounds = []
586
+
587
+ for proposal in proposals:
588
+ if (proposal.best_iou < incomplete_iou_threshold
589
+ and proposal.overlap_self > incomplete_overlap_threshold):
590
+ incompletes.append(proposal)
591
+ elif (proposal.best_iou < background_iou_threshold
592
+ and proposal.coverage > background_coverage_threshold):
593
+ backgrounds.append(proposal)
594
+
595
+ return incompletes, backgrounds
596
+
597
+ def _video_centric_sampling(self, record):
598
+ """Sample proposals from the this video instance.
599
+
600
+ Args:
601
+ record (dict): Information of the video instance(video_info[idx]).
602
+ key: frame_dir, video_id, total_frames,
603
+ gts: List of groundtruth instances(:obj:`SSNInstance`).
604
+ proposals: List of proposal instances(:obj:`SSNInstance`).
605
+ """
606
+ positives = self.get_positives(record['gts'], record['proposals'],
607
+ self.assigner.positive_iou_threshold,
608
+ self.sampler.add_gt_as_proposals)
609
+ incompletes, backgrounds = self.get_negatives(
610
+ record['proposals'], self.assigner.incomplete_iou_threshold,
611
+ self.assigner.background_iou_threshold,
612
+ self.assigner.background_coverage_threshold,
613
+ self.assigner.incomplete_overlap_threshold)
614
+
615
+ def sample_video_proposals(proposal_type, video_id, video_pool,
616
+ num_requested_proposals, dataset_pool):
617
+ """This method will sample proposals from the this video pool. If
618
+ the video pool is empty, it will fetch from the dataset pool
619
+ (collect proposal of the entire dataset).
620
+
621
+ Args:
622
+ proposal_type (int): Type id of proposal.
623
+ Positive/Foreground: 0
624
+ Negative:
625
+ Incomplete: 1
626
+ Background: 2
627
+ video_id (str): Name of the video.
628
+ video_pool (list): Pool comprised of proposals in this video.
629
+ num_requested_proposals (int): Number of proposals
630
+ to be sampled.
631
+ dataset_pool (list): Proposals of the entire dataset.
632
+
633
+ Returns:
634
+ list[(str, :obj:`SSNInstance`), int]:
635
+ video_id (str): Name of the video.
636
+ :obj:`SSNInstance`: Instance of class SSNInstance.
637
+ proposal_type (int): Type of proposal.
638
+ """
639
+
640
+ if len(video_pool) == 0:
641
+ idx = np.random.choice(
642
+ len(dataset_pool), num_requested_proposals, replace=False)
643
+ return [(dataset_pool[x], proposal_type) for x in idx]
644
+
645
+ replicate = len(video_pool) < num_requested_proposals
646
+ idx = np.random.choice(
647
+ len(video_pool), num_requested_proposals, replace=replicate)
648
+ return [((video_id, video_pool[x]), proposal_type) for x in idx]
649
+
650
+ out_proposals = []
651
+ out_proposals.extend(
652
+ sample_video_proposals(0, record['video_id'], positives,
653
+ self.positive_per_video,
654
+ self.positive_pool))
655
+ out_proposals.extend(
656
+ sample_video_proposals(1, record['video_id'], incompletes,
657
+ self.incomplete_per_video,
658
+ self.incomplete_pool))
659
+ out_proposals.extend(
660
+ sample_video_proposals(2, record['video_id'], backgrounds,
661
+ self.background_per_video,
662
+ self.background_pool))
663
+
664
+ return out_proposals
665
+
666
+ def _random_sampling(self):
667
+ """Randomly sample proposals from the entire dataset."""
668
+ out_proposals = []
669
+
670
+ positive_idx = np.random.choice(
671
+ len(self.positive_pool),
672
+ self.positive_per_video,
673
+ replace=len(self.positive_pool) < self.positive_per_video)
674
+ out_proposals.extend([(self.positive_pool[x], 0)
675
+ for x in positive_idx])
676
+ incomplete_idx = np.random.choice(
677
+ len(self.incomplete_pool),
678
+ self.incomplete_per_video,
679
+ replace=len(self.incomplete_pool) < self.incomplete_per_video)
680
+ out_proposals.extend([(self.incomplete_pool[x], 1)
681
+ for x in incomplete_idx])
682
+ background_idx = np.random.choice(
683
+ len(self.background_pool),
684
+ self.background_per_video,
685
+ replace=len(self.background_pool) < self.background_per_video)
686
+ out_proposals.extend([(self.background_pool[x], 2)
687
+ for x in background_idx])
688
+
689
+ return out_proposals
690
+
691
+ def _get_stage(self, proposal, num_frames):
692
+ """Fetch the scale factor of starting and ending stage and get the
693
+ stage split.
694
+
695
+ Args:
696
+ proposal (:obj:`SSNInstance`): Proposal instance.
697
+ num_frames (int): Total frames of the video.
698
+
699
+ Returns:
700
+ tuple[float, float, list]: (starting_scale_factor,
701
+ ending_scale_factor, stage_split), starting_scale_factor is
702
+ the ratio of the effective sampling length to augment length
703
+ in starting stage, ending_scale_factor is the ratio of the
704
+ effective sampling length to augment length in ending stage,
705
+ stage_split is ending segment id of starting, course and
706
+ ending stage.
707
+ """
708
+ # proposal interval: [start_frame, end_frame)
709
+ start_frame = proposal.start_frame
710
+ end_frame = proposal.end_frame
711
+ ori_clip_len = self.clip_len * self.frame_interval
712
+
713
+ duration = end_frame - start_frame
714
+ assert duration != 0
715
+
716
+ valid_starting = max(0,
717
+ start_frame - int(duration * self.aug_ratio[0]))
718
+ valid_ending = min(num_frames - ori_clip_len + 1,
719
+ end_frame - 1 + int(duration * self.aug_ratio[1]))
720
+
721
+ valid_starting_length = start_frame - valid_starting - ori_clip_len
722
+ valid_ending_length = (valid_ending - end_frame + 1) - ori_clip_len
723
+
724
+ starting_scale_factor = ((valid_starting_length + ori_clip_len + 1) /
725
+ (duration * self.aug_ratio[0]))
726
+ ending_scale_factor = (valid_ending_length + ori_clip_len + 1) / (
727
+ duration * self.aug_ratio[1])
728
+
729
+ aug_start, aug_end = self.aug_segments
730
+ stage_split = [
731
+ aug_start, aug_start + self.body_segments,
732
+ aug_start + self.body_segments + aug_end
733
+ ]
734
+
735
+ return starting_scale_factor, ending_scale_factor, stage_split
736
+
737
+ def _compute_reg_normalize_constants(self):
738
+ """Compute regression target normalized constants."""
739
+ if self.verbose:
740
+ self.logger.info('Compute regression target normalized constants')
741
+ targets = []
742
+ for video_info in self.video_infos:
743
+ positives = self.get_positives(
744
+ video_info['gts'], video_info['proposals'],
745
+ self.assigner.positive_iou_threshold, False)
746
+ for positive in positives:
747
+ targets.append(list(positive.regression_targets))
748
+
749
+ return np.array((np.mean(targets, axis=0), np.std(targets, axis=0)))
750
+
751
+ def prepare_train_frames(self, idx):
752
+ """Prepare the frames for training given the index."""
753
+ results = copy.deepcopy(self.video_infos[idx])
754
+ results['filename_tmpl'] = self.filename_tmpl
755
+ results['modality'] = self.modality
756
+ results['start_index'] = self.start_index
757
+
758
+ if self.video_centric:
759
+ # yapf: disable
760
+ results['out_proposals'] = self._video_centric_sampling(self.video_infos[idx]) # noqa: E501
761
+ # yapf: enable
762
+ else:
763
+ results['out_proposals'] = self._random_sampling()
764
+
765
+ out_proposal_scale_factor = []
766
+ out_proposal_type = []
767
+ out_proposal_labels = []
768
+ out_proposal_reg_targets = []
769
+
770
+ for _, proposal in enumerate(results['out_proposals']):
771
+ # proposal: [(video_id, SSNInstance), proposal_type]
772
+ num_frames = proposal[0][1].num_video_frames
773
+
774
+ (starting_scale_factor, ending_scale_factor,
775
+ _) = self._get_stage(proposal[0][1], num_frames)
776
+
777
+ # proposal[1]: Type id of proposal.
778
+ # Positive/Foreground: 0
779
+ # Negative:
780
+ # Incomplete: 1
781
+ # Background: 2
782
+
783
+ # Positivte/Foreground proposal
784
+ if proposal[1] == 0:
785
+ label = proposal[0][1].label
786
+ # Incomplete proposal
787
+ elif proposal[1] == 1:
788
+ label = proposal[0][1].label
789
+ # Background proposal
790
+ elif proposal[1] == 2:
791
+ label = 0
792
+ else:
793
+ raise ValueError(f'Proposal type should be 0, 1, or 2,'
794
+ f'but got {proposal[1]}')
795
+ out_proposal_scale_factor.append(
796
+ [starting_scale_factor, ending_scale_factor])
797
+ if not isinstance(label, int):
798
+ raise TypeError(f'proposal_label must be an int,'
799
+ f'but got {type(label)}')
800
+ out_proposal_labels.append(label)
801
+ out_proposal_type.append(proposal[1])
802
+
803
+ reg_targets = proposal[0][1].regression_targets
804
+ if proposal[1] == 0:
805
+ # Normalize regression targets of positive proposals.
806
+ reg_targets = ((reg_targets[0] - self.reg_norm_consts[0][0]) /
807
+ self.reg_norm_consts[1][0],
808
+ (reg_targets[1] - self.reg_norm_consts[0][1]) /
809
+ self.reg_norm_consts[1][1])
810
+ out_proposal_reg_targets.append(reg_targets)
811
+
812
+ results['reg_targets'] = np.array(
813
+ out_proposal_reg_targets, dtype=np.float32)
814
+ results['proposal_scale_factor'] = np.array(
815
+ out_proposal_scale_factor, dtype=np.float32)
816
+ results['proposal_labels'] = np.array(out_proposal_labels)
817
+ results['proposal_type'] = np.array(out_proposal_type)
818
+
819
+ return self.pipeline(results)
820
+
821
+ def prepare_test_frames(self, idx):
822
+ """Prepare the frames for testing given the index."""
823
+ results = copy.deepcopy(self.video_infos[idx])
824
+ results['filename_tmpl'] = self.filename_tmpl
825
+ results['modality'] = self.modality
826
+ results['start_index'] = self.start_index
827
+
828
+ proposals = results['proposals']
829
+ num_frames = results['total_frames']
830
+ ori_clip_len = self.clip_len * self.frame_interval
831
+ frame_ticks = np.arange(
832
+ 0, num_frames - ori_clip_len, self.test_interval, dtype=int) + 1
833
+
834
+ num_sampled_frames = len(frame_ticks)
835
+
836
+ if len(proposals) == 0:
837
+ proposals.append(SSNInstance(0, num_frames - 1, num_frames))
838
+
839
+ relative_proposal_list = []
840
+ proposal_tick_list = []
841
+ scale_factor_list = []
842
+
843
+ for proposal in proposals:
844
+ relative_proposal = (proposal.start_frame / num_frames,
845
+ proposal.end_frame / num_frames)
846
+ relative_duration = relative_proposal[1] - relative_proposal[0]
847
+ relative_starting_duration = relative_duration * self.aug_ratio[0]
848
+ relative_ending_duration = relative_duration * self.aug_ratio[1]
849
+ relative_starting = (
850
+ relative_proposal[0] - relative_starting_duration)
851
+ relative_ending = relative_proposal[1] + relative_ending_duration
852
+
853
+ real_relative_starting = max(0.0, relative_starting)
854
+ real_relative_ending = min(1.0, relative_ending)
855
+
856
+ starting_scale_factor = (
857
+ (relative_proposal[0] - real_relative_starting) /
858
+ relative_starting_duration)
859
+ ending_scale_factor = (
860
+ (real_relative_ending - relative_proposal[1]) /
861
+ relative_ending_duration)
862
+
863
+ proposal_ranges = (real_relative_starting, *relative_proposal,
864
+ real_relative_ending)
865
+ proposal_ticks = (np.array(proposal_ranges) *
866
+ num_sampled_frames).astype(np.int32)
867
+
868
+ relative_proposal_list.append(relative_proposal)
869
+ proposal_tick_list.append(proposal_ticks)
870
+ scale_factor_list.append(
871
+ (starting_scale_factor, ending_scale_factor))
872
+
873
+ results['relative_proposal_list'] = np.array(
874
+ relative_proposal_list, dtype=np.float32)
875
+ results['scale_factor_list'] = np.array(
876
+ scale_factor_list, dtype=np.float32)
877
+ results['proposal_tick_list'] = np.array(
878
+ proposal_tick_list, dtype=np.int32)
879
+ results['reg_norm_consts'] = self.reg_norm_consts
880
+
881
+ return self.pipeline(results)
TransRAC/mmaction/datasets/video_dataset.py ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+
3
+ from .base import BaseDataset
4
+ from .builder import DATASETS
5
+
6
+
7
+ @DATASETS.register_module()
8
+ class VideoDataset(BaseDataset):
9
+ """Video dataset for action recognition.
10
+
11
+ The dataset loads raw videos and apply specified transforms to return a
12
+ dict containing the frame tensors and other information.
13
+
14
+ The ann_file is a text file with multiple lines, and each line indicates
15
+ a sample video with the filepath and label, which are split with a
16
+ whitespace. Example of a annotation file:
17
+
18
+ .. code-block:: txt
19
+
20
+ some/path/000.mp4 1
21
+ some/path/001.mp4 1
22
+ some/path/002.mp4 2
23
+ some/path/003.mp4 2
24
+ some/path/004.mp4 3
25
+ some/path/005.mp4 3
26
+
27
+
28
+ Args:
29
+ ann_file (str): Path to the annotation file.
30
+ pipeline (list[dict | callable]): A sequence of data transforms.
31
+ start_index (int): Specify a start index for frames in consideration of
32
+ different filename format. However, when taking videos as input,
33
+ it should be set to 0, since frames loaded from videos count
34
+ from 0. Default: 0.
35
+ **kwargs: Keyword arguments for ``BaseDataset``.
36
+ """
37
+
38
+ def __init__(self, ann_file, pipeline, start_index=0, **kwargs):
39
+ super().__init__(ann_file, pipeline, start_index=start_index, **kwargs)
40
+
41
+ def load_annotations(self):
42
+ """Load annotation file to get video information."""
43
+ if self.ann_file.endswith('.json'):
44
+ return self.load_json_annotations()
45
+
46
+ video_infos = []
47
+ with open(self.ann_file, 'r') as fin:
48
+ for line in fin:
49
+ line_split = line.strip().split()
50
+ if self.multi_class:
51
+ assert self.num_classes is not None
52
+ filename, label = line_split[0], line_split[1:]
53
+ label = list(map(int, label))
54
+ else:
55
+ filename, label = line_split
56
+ label = int(label)
57
+ if self.data_prefix is not None:
58
+ filename = osp.join(self.data_prefix, filename)
59
+ video_infos.append(dict(filename=filename, label=label))
60
+ return video_infos
TransRAC/mmaction/localization/__init__.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from .bsn_utils import generate_bsp_feature, generate_candidate_proposals
2
+ from .proposal_utils import soft_nms, temporal_iop, temporal_iou
3
+ from .ssn_utils import (eval_ap, load_localize_proposal_file,
4
+ perform_regression, temporal_nms)
5
+
6
+ __all__ = [
7
+ 'generate_candidate_proposals', 'generate_bsp_feature', 'temporal_iop',
8
+ 'temporal_iou', 'soft_nms', 'load_localize_proposal_file',
9
+ 'perform_regression', 'temporal_nms', 'eval_ap'
10
+ ]
TransRAC/mmaction/localization/bsn_utils.py ADDED
@@ -0,0 +1,267 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os.path as osp
2
+
3
+ import numpy as np
4
+
5
+ from .proposal_utils import temporal_iop, temporal_iou
6
+
7
+
8
+ def generate_candidate_proposals(video_list,
9
+ video_infos,
10
+ tem_results_dir,
11
+ temporal_scale,
12
+ peak_threshold,
13
+ tem_results_ext='.csv',
14
+ result_dict=None):
15
+ """Generate Candidate Proposals with given temporal evalutation results.
16
+ Each proposal file will contain:
17
+ 'tmin,tmax,tmin_score,tmax_score,score,match_iou,match_ioa'.
18
+
19
+ Args:
20
+ video_list (list[int]): List of video indexs to generate proposals.
21
+ video_infos (list[dict]): List of video_info dict that contains
22
+ 'video_name', 'duration_frame', 'duration_second',
23
+ 'feature_frame', and 'annotations'.
24
+ tem_results_dir (str): Directory to load temporal evaluation
25
+ results.
26
+ temporal_scale (int): The number (scale) on temporal axis.
27
+ peak_threshold (float): The threshold for proposal generation.
28
+ tem_results_ext (str): File extension for temporal evaluation
29
+ model output. Default: '.csv'.
30
+ result_dict (dict | None): The dict to save the results. Default: None.
31
+
32
+ Returns:
33
+ dict: A dict contains video_name as keys and proposal list as value.
34
+ If result_dict is not None, save the results to it.
35
+ """
36
+ if tem_results_ext != '.csv':
37
+ raise NotImplementedError('Only support csv format now.')
38
+
39
+ tscale = temporal_scale
40
+ tgap = 1. / tscale
41
+ proposal_dict = {}
42
+ for video_index in video_list:
43
+ video_name = video_infos[video_index]['video_name']
44
+ tem_path = osp.join(tem_results_dir, video_name + tem_results_ext)
45
+ tem_results = np.loadtxt(
46
+ tem_path, dtype=np.float32, delimiter=',', skiprows=1)
47
+ start_scores = tem_results[:, 1]
48
+ end_scores = tem_results[:, 2]
49
+
50
+ max_start = max(start_scores)
51
+ max_end = max(end_scores)
52
+
53
+ start_bins = np.zeros(len(start_scores))
54
+ start_bins[[0, -1]] = 1
55
+ end_bins = np.zeros(len(end_scores))
56
+ end_bins[[0, -1]] = 1
57
+ for idx in range(1, tscale - 1):
58
+ if start_scores[idx] > start_scores[
59
+ idx + 1] and start_scores[idx] > start_scores[idx - 1]:
60
+ start_bins[idx] = 1
61
+ elif start_scores[idx] > (peak_threshold * max_start):
62
+ start_bins[idx] = 1
63
+ if end_scores[idx] > end_scores[
64
+ idx + 1] and end_scores[idx] > end_scores[idx - 1]:
65
+ end_bins[idx] = 1
66
+ elif end_scores[idx] > (peak_threshold * max_end):
67
+ end_bins[idx] = 1
68
+
69
+ tmin_list = []
70
+ tmin_score_list = []
71
+ tmax_list = []
72
+ tmax_score_list = []
73
+ for idx in range(tscale):
74
+ if start_bins[idx] == 1:
75
+ tmin_list.append(tgap / 2 + tgap * idx)
76
+ tmin_score_list.append(start_scores[idx])
77
+ if end_bins[idx] == 1:
78
+ tmax_list.append(tgap / 2 + tgap * idx)
79
+ tmax_score_list.append(end_scores[idx])
80
+
81
+ new_props = []
82
+ for tmax, tmax_score in zip(tmax_list, tmax_score_list):
83
+ for tmin, tmin_score in zip(tmin_list, tmin_score_list):
84
+ if tmin >= tmax:
85
+ break
86
+ new_props.append([tmin, tmax, tmin_score, tmax_score])
87
+
88
+ new_props = np.stack(new_props)
89
+
90
+ score = (new_props[:, 2] * new_props[:, 3]).reshape(-1, 1)
91
+ new_props = np.concatenate((new_props, score), axis=1)
92
+
93
+ new_props = new_props[new_props[:, -1].argsort()[::-1]]
94
+ video_info = video_infos[video_index]
95
+ video_frame = video_info['duration_frame']
96
+ video_second = video_info['duration_second']
97
+ feature_frame = video_info['feature_frame']
98
+ corrected_second = float(feature_frame) / video_frame * video_second
99
+
100
+ gt_tmins = []
101
+ gt_tmaxs = []
102
+ for annotations in video_info['annotations']:
103
+ gt_tmins.append(annotations['segment'][0] / corrected_second)
104
+ gt_tmaxs.append(annotations['segment'][1] / corrected_second)
105
+
106
+ new_iou_list = []
107
+ new_ioa_list = []
108
+ for new_prop in new_props:
109
+ new_iou = max(
110
+ temporal_iou(new_prop[0], new_prop[1], gt_tmins, gt_tmaxs))
111
+ new_ioa = max(
112
+ temporal_iop(new_prop[0], new_prop[1], gt_tmins, gt_tmaxs))
113
+ new_iou_list.append(new_iou)
114
+ new_ioa_list.append(new_ioa)
115
+
116
+ new_iou_list = np.array(new_iou_list).reshape(-1, 1)
117
+ new_ioa_list = np.array(new_ioa_list).reshape(-1, 1)
118
+ new_props = np.concatenate((new_props, new_iou_list), axis=1)
119
+ new_props = np.concatenate((new_props, new_ioa_list), axis=1)
120
+ proposal_dict[video_name] = new_props
121
+ if result_dict is not None:
122
+ result_dict[video_name] = new_props
123
+ return proposal_dict
124
+
125
+
126
+ def generate_bsp_feature(video_list,
127
+ video_infos,
128
+ tem_results_dir,
129
+ pgm_proposals_dir,
130
+ top_k=1000,
131
+ bsp_boundary_ratio=0.2,
132
+ num_sample_start=8,
133
+ num_sample_end=8,
134
+ num_sample_action=16,
135
+ num_sample_interp=3,
136
+ tem_results_ext='.csv',
137
+ pgm_proposal_ext='.csv',
138
+ result_dict=None):
139
+ """Generate Boundary-Sensitive Proposal Feature with given proposals.
140
+
141
+ Args:
142
+ video_list (list[int]): List of video indexs to generate bsp_feature.
143
+ video_infos (list[dict]): List of video_info dict that contains
144
+ 'video_name'.
145
+ tem_results_dir (str): Directory to load temporal evaluation
146
+ results.
147
+ pgm_proposals_dir (str): Directory to load proposals.
148
+ top_k (int): Number of proposals to be considered. Default: 1000
149
+ bsp_boundary_ratio (float): Ratio for proposal boundary
150
+ (start/end). Default: 0.2.
151
+ num_sample_start (int): Num of samples for actionness in
152
+ start region. Default: 8.
153
+ num_sample_end (int): Num of samples for actionness in end region.
154
+ Default: 8.
155
+ num_sample_action (int): Num of samples for actionness in center
156
+ region. Default: 16.
157
+ num_sample_interp (int): Num of samples for interpolation for
158
+ each sample point. Default: 3.
159
+ tem_results_ext (str): File extension for temporal evaluation
160
+ model output. Default: '.csv'.
161
+ pgm_proposal_ext (str): File extension for proposals. Default: '.csv'.
162
+ result_dict (dict | None): The dict to save the results. Default: None.
163
+
164
+ Returns:
165
+ bsp_feature_dict (dict): A dict contains video_name as keys and
166
+ bsp_feature as value. If result_dict is not None, save the
167
+ results to it.
168
+ """
169
+ if tem_results_ext != '.csv' or pgm_proposal_ext != '.csv':
170
+ raise NotImplementedError('Only support csv format now.')
171
+
172
+ bsp_feature_dict = {}
173
+ for video_index in video_list:
174
+ video_name = video_infos[video_index]['video_name']
175
+
176
+ # Load temporal evaluation results
177
+ tem_path = osp.join(tem_results_dir, video_name + tem_results_ext)
178
+ tem_results = np.loadtxt(
179
+ tem_path, dtype=np.float32, delimiter=',', skiprows=1)
180
+ score_action = tem_results[:, 0]
181
+ seg_tmins = tem_results[:, 3]
182
+ seg_tmaxs = tem_results[:, 4]
183
+ video_scale = len(tem_results)
184
+ video_gap = seg_tmaxs[0] - seg_tmins[0]
185
+ video_extend = int(video_scale / 4 + 10)
186
+
187
+ # Load proposals results
188
+ proposal_path = osp.join(pgm_proposals_dir,
189
+ video_name + pgm_proposal_ext)
190
+ pgm_proposals = np.loadtxt(
191
+ proposal_path, dtype=np.float32, delimiter=',', skiprows=1)
192
+ pgm_proposals = pgm_proposals[:top_k]
193
+
194
+ # Generate temporal sample points
195
+ boundary_zeros = np.zeros([video_extend])
196
+ score_action = np.concatenate(
197
+ (boundary_zeros, score_action, boundary_zeros))
198
+ begin_tp = []
199
+ middle_tp = []
200
+ end_tp = []
201
+ for i in range(video_extend):
202
+ begin_tp.append(-video_gap / 2 -
203
+ (video_extend - 1 - i) * video_gap)
204
+ end_tp.append(video_gap / 2 + seg_tmaxs[-1] + i * video_gap)
205
+ for i in range(video_scale):
206
+ middle_tp.append(video_gap / 2 + i * video_gap)
207
+ t_points = begin_tp + middle_tp + end_tp
208
+
209
+ bsp_feature = []
210
+ for pgm_proposal in pgm_proposals:
211
+ tmin = pgm_proposal[0]
212
+ tmax = pgm_proposal[1]
213
+
214
+ tlen = tmax - tmin
215
+ # Temporal range for start
216
+ tmin_0 = tmin - tlen * bsp_boundary_ratio
217
+ tmin_1 = tmin + tlen * bsp_boundary_ratio
218
+ # Temporal range for end
219
+ tmax_0 = tmax - tlen * bsp_boundary_ratio
220
+ tmax_1 = tmax + tlen * bsp_boundary_ratio
221
+
222
+ # Generate features at start boundary
223
+ tlen_start = (tmin_1 - tmin_0) / (num_sample_start - 1)
224
+ tlen_start_sample = tlen_start / num_sample_interp
225
+ t_new = [
226
+ tmin_0 - tlen_start / 2 + tlen_start_sample * i
227
+ for i in range(num_sample_start * num_sample_interp + 1)
228
+ ]
229
+ y_new_start_action = np.interp(t_new, t_points, score_action)
230
+ y_new_start = [
231
+ np.mean(y_new_start_action[i * num_sample_interp:(i + 1) *
232
+ num_sample_interp + 1])
233
+ for i in range(num_sample_start)
234
+ ]
235
+ # Generate features at end boundary
236
+ tlen_end = (tmax_1 - tmax_0) / (num_sample_end - 1)
237
+ tlen_end_sample = tlen_end / num_sample_interp
238
+ t_new = [
239
+ tmax_0 - tlen_end / 2 + tlen_end_sample * i
240
+ for i in range(num_sample_end * num_sample_interp + 1)
241
+ ]
242
+ y_new_end_action = np.interp(t_new, t_points, score_action)
243
+ y_new_end = [
244
+ np.mean(y_new_end_action[i * num_sample_interp:(i + 1) *
245
+ num_sample_interp + 1])
246
+ for i in range(num_sample_end)
247
+ ]
248
+ # Generate features for action
249
+ tlen_action = (tmax - tmin) / (num_sample_action - 1)
250
+ tlen_action_sample = tlen_action / num_sample_interp
251
+ t_new = [
252
+ tmin - tlen_action / 2 + tlen_action_sample * i
253
+ for i in range(num_sample_action * num_sample_interp + 1)
254
+ ]
255
+ y_new_action = np.interp(t_new, t_points, score_action)
256
+ y_new_action = [
257
+ np.mean(y_new_action[i * num_sample_interp:(i + 1) *
258
+ num_sample_interp + 1])
259
+ for i in range(num_sample_action)
260
+ ]
261
+ feature = np.concatenate([y_new_action, y_new_start, y_new_end])
262
+ bsp_feature.append(feature)
263
+ bsp_feature = np.array(bsp_feature)
264
+ bsp_feature_dict[video_name] = bsp_feature
265
+ if result_dict is not None:
266
+ result_dict[video_name] = bsp_feature
267
+ return bsp_feature_dict
TransRAC/mmaction/localization/proposal_utils.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+
4
+ def temporal_iou(proposal_min, proposal_max, gt_min, gt_max):
5
+ """Compute IoU score between a groundtruth bbox and the proposals.
6
+
7
+ Args:
8
+ proposal_min (list[float]): List of temporal anchor min.
9
+ proposal_max (list[float]): List of temporal anchor max.
10
+ gt_min (float): Groundtruth temporal box min.
11
+ gt_max (float): Groundtruth temporal box max.
12
+
13
+ Returns:
14
+ list[float]: List of iou scores.
15
+ """
16
+ len_anchors = proposal_max - proposal_min
17
+ int_tmin = np.maximum(proposal_min, gt_min)
18
+ int_tmax = np.minimum(proposal_max, gt_max)
19
+ inter_len = np.maximum(int_tmax - int_tmin, 0.)
20
+ union_len = len_anchors - inter_len + gt_max - gt_min
21
+ jaccard = np.divide(inter_len, union_len)
22
+ return jaccard
23
+
24
+
25
+ def temporal_iop(proposal_min, proposal_max, gt_min, gt_max):
26
+ """Compute IoP score between a groundtruth bbox and the proposals.
27
+
28
+ Compute the IoP which is defined as the overlap ratio with
29
+ groundtruth proportional to the duration of this proposal.
30
+
31
+ Args:
32
+ proposal_min (list[float]): List of temporal anchor min.
33
+ proposal_max (list[float]): List of temporal anchor max.
34
+ gt_min (float): Groundtruth temporal box min.
35
+ gt_max (float): Groundtruth temporal box max.
36
+
37
+ Returns:
38
+ list[float]: List of intersection over anchor scores.
39
+ """
40
+ len_anchors = np.array(proposal_max - proposal_min)
41
+ int_tmin = np.maximum(proposal_min, gt_min)
42
+ int_tmax = np.minimum(proposal_max, gt_max)
43
+ inter_len = np.maximum(int_tmax - int_tmin, 0.)
44
+ scores = np.divide(inter_len, len_anchors)
45
+ return scores
46
+
47
+
48
+ def soft_nms(proposals, alpha, low_threshold, high_threshold, top_k):
49
+ """Soft NMS for temporal proposals.
50
+
51
+ Args:
52
+ proposals (np.ndarray): Proposals generated by network.
53
+ alpha (float): Alpha value of Gaussian decaying function.
54
+ low_threshold (float): Low threshold for soft nms.
55
+ high_threshold (float): High threshold for soft nms.
56
+ top_k (int): Top k values to be considered.
57
+
58
+ Returns:
59
+ np.ndarray: The updated proposals.
60
+ """
61
+ proposals = proposals[proposals[:, -1].argsort()[::-1]]
62
+ tstart = list(proposals[:, 0])
63
+ tend = list(proposals[:, 1])
64
+ tscore = list(proposals[:, -1])
65
+ rstart = []
66
+ rend = []
67
+ rscore = []
68
+
69
+ while len(tscore) > 0 and len(rscore) <= top_k:
70
+ max_index = np.argmax(tscore)
71
+ max_width = tend[max_index] - tstart[max_index]
72
+ iou_list = temporal_iou(tstart[max_index], tend[max_index],
73
+ np.array(tstart), np.array(tend))
74
+ iou_exp_list = np.exp(-np.square(iou_list) / alpha)
75
+
76
+ for idx, _ in enumerate(tscore):
77
+ if idx != max_index:
78
+ current_iou = iou_list[idx]
79
+ if current_iou > low_threshold + (high_threshold -
80
+ low_threshold) * max_width:
81
+ tscore[idx] = tscore[idx] * iou_exp_list[idx]
82
+
83
+ rstart.append(tstart[max_index])
84
+ rend.append(tend[max_index])
85
+ rscore.append(tscore[max_index])
86
+ tstart.pop(max_index)
87
+ tend.pop(max_index)
88
+ tscore.pop(max_index)
89
+
90
+ rstart = np.array(rstart).reshape(-1, 1)
91
+ rend = np.array(rend).reshape(-1, 1)
92
+ rscore = np.array(rscore).reshape(-1, 1)
93
+ new_proposals = np.concatenate((rstart, rend, rscore), axis=1)
94
+ return new_proposals
TransRAC/mmaction/localization/ssn_utils.py ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import groupby
2
+
3
+ import numpy as np
4
+
5
+ from ..core import average_precision_at_temporal_iou
6
+ from . import temporal_iou
7
+
8
+
9
+ def load_localize_proposal_file(filename):
10
+ """Load the proposal file and split it into many parts which contain one
11
+ video's information separately.
12
+
13
+ Args:
14
+ filename(str): Path to the proposal file.
15
+
16
+ Returns:
17
+ list: List of all videos' information.
18
+ """
19
+ lines = list(open(filename))
20
+
21
+ # Split the proposal file into many parts which contain one video's
22
+ # information separately.
23
+ groups = groupby(lines, lambda x: x.startswith('#'))
24
+
25
+ video_infos = [[x.strip() for x in list(g)] for k, g in groups if not k]
26
+
27
+ def parse_group(video_info):
28
+ """Parse the video's information.
29
+
30
+ Template information of a video in a standard file:
31
+ # index
32
+ video_id
33
+ num_frames
34
+ fps
35
+ num_gts
36
+ label, start_frame, end_frame
37
+ label, start_frame, end_frame
38
+ ...
39
+ num_proposals
40
+ label, best_iou, overlap_self, start_frame, end_frame
41
+ label, best_iou, overlap_self, start_frame, end_frame
42
+ ...
43
+
44
+ Example of a standard annotation file:
45
+
46
+ .. code-block:: txt
47
+
48
+ # 0
49
+ video_validation_0000202
50
+ 5666
51
+ 1
52
+ 3
53
+ 8 130 185
54
+ 8 832 1136
55
+ 8 1303 1381
56
+ 5
57
+ 8 0.0620 0.0620 790 5671
58
+ 8 0.1656 0.1656 790 2619
59
+ 8 0.0833 0.0833 3945 5671
60
+ 8 0.0960 0.0960 4173 5671
61
+ 8 0.0614 0.0614 3327 5671
62
+
63
+ Args:
64
+ video_info (list): Information of the video.
65
+
66
+ Returns:
67
+ tuple[str, int, list, list]:
68
+ video_id (str): Name of the video.
69
+ num_frames (int): Number of frames in the video.
70
+ gt_boxes (list): List of the information of gt boxes.
71
+ proposal_boxes (list): List of the information of
72
+ proposal boxes.
73
+ """
74
+ offset = 0
75
+ video_id = video_info[offset]
76
+ offset += 1
77
+
78
+ num_frames = int(float(video_info[1]) * float(video_info[2]))
79
+ num_gts = int(video_info[3])
80
+ offset = 4
81
+
82
+ gt_boxes = [x.split() for x in video_info[offset:offset + num_gts]]
83
+ offset += num_gts
84
+ num_proposals = int(video_info[offset])
85
+ offset += 1
86
+ proposal_boxes = [
87
+ x.split() for x in video_info[offset:offset + num_proposals]
88
+ ]
89
+
90
+ return video_id, num_frames, gt_boxes, proposal_boxes
91
+
92
+ return [parse_group(video_info) for video_info in video_infos]
93
+
94
+
95
+ def perform_regression(detections):
96
+ """Perform regression on detection results.
97
+
98
+ Args:
99
+ detections (list): Detection results before regression.
100
+
101
+ Returns:
102
+ list: Detection results after regression.
103
+ """
104
+ starts = detections[:, 0]
105
+ ends = detections[:, 1]
106
+ centers = (starts + ends) / 2
107
+ durations = ends - starts
108
+
109
+ new_centers = centers + durations * detections[:, 3]
110
+ new_durations = durations * np.exp(detections[:, 4])
111
+
112
+ new_detections = np.concatenate(
113
+ (np.clip(new_centers - new_durations / 2, 0,
114
+ 1)[:, None], np.clip(new_centers + new_durations / 2, 0,
115
+ 1)[:, None], detections[:, 2:]),
116
+ axis=1)
117
+ return new_detections
118
+
119
+
120
+ def temporal_nms(detections, threshold):
121
+ """Parse the video's information.
122
+
123
+ Args:
124
+ detections (list): Detection results before NMS.
125
+ threshold (float): Threshold of NMS.
126
+
127
+ Returns:
128
+ list: Detection results after NMS.
129
+ """
130
+ starts = detections[:, 0]
131
+ ends = detections[:, 1]
132
+ scores = detections[:, 2]
133
+
134
+ order = scores.argsort()[::-1]
135
+
136
+ keep = []
137
+ while order.size > 0:
138
+ i = order[0]
139
+ keep.append(i)
140
+ ious = temporal_iou(starts[order[1:]], ends[order[1:]], starts[i],
141
+ ends[i])
142
+ idxs = np.where(ious <= threshold)[0]
143
+ order = order[idxs + 1]
144
+
145
+ return detections[keep, :]
146
+
147
+
148
+ def eval_ap(detections, gt_by_cls, iou_range):
149
+ """Evaluate average precisions.
150
+
151
+ Args:
152
+ detections (dict): Results of detections.
153
+ gt_by_cls (dict): Information of groudtruth.
154
+ iou_range (list): Ranges of iou.
155
+
156
+ Returns:
157
+ list: Average precision values of classes at ious.
158
+ """
159
+ ap_values = np.zeros((len(detections), len(iou_range)))
160
+
161
+ for iou_idx, min_overlap in enumerate(iou_range):
162
+ for class_idx, _ in enumerate(detections):
163
+ ap = average_precision_at_temporal_iou(gt_by_cls[class_idx],
164
+ detections[class_idx],
165
+ [min_overlap])
166
+ ap_values[class_idx, iou_idx] = ap
167
+
168
+ return ap_values
TransRAC/mmaction/utils/decorators.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from types import MethodType
2
+
3
+
4
+ def import_module_error_func(module_name):
5
+ """When a function is imported incorrectly due to a missing module, raise
6
+ an import error when the function is called."""
7
+
8
+ def decorate(func):
9
+
10
+ def new_func(*args, **kwargs):
11
+ raise ImportError(
12
+ f'Please install {module_name} to use {func.__name__}.')
13
+
14
+ return new_func
15
+
16
+ return decorate
17
+
18
+
19
+ def import_module_error_class(module_name):
20
+ """When a class is imported incorrectly due to a missing module, raise an
21
+ import error when the class is instantiated."""
22
+
23
+ def decorate(cls):
24
+
25
+ def import_error_init(*args, **kwargs):
26
+ raise ImportError(
27
+ f'Please install {module_name} to use {cls.__name__}.')
28
+
29
+ cls.__init__ = MethodType(import_error_init, cls)
30
+ return cls
31
+
32
+ return decorate
TransRAC/mmaction/utils/logger.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+
3
+ from mmcv.utils import get_logger
4
+
5
+
6
+ def get_root_logger(log_file=None, log_level=logging.INFO):
7
+ """Use ``get_logger`` method in mmcv to get the root logger.
8
+
9
+ The logger will be initialized if it has not been initialized. By default a
10
+ StreamHandler will be added. If ``log_file`` is specified, a FileHandler
11
+ will also be added. The name of the root logger is the top-level package
12
+ name, e.g., "mmaction".
13
+
14
+ Args:
15
+ log_file (str | None): The log filename. If specified, a FileHandler
16
+ will be added to the root logger.
17
+ log_level (int): The root logger level. Note that only the process of
18
+ rank 0 is affected, while other processes will set the level to
19
+ "Error" and be silent most of the time.
20
+
21
+ Returns:
22
+ :obj:`logging.Logger`: The root logger.
23
+ """
24
+ return get_logger(__name__.split('.')[0], log_file, log_level)
TransRAC/mmaction/utils/optimizer.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from mmcv.runner import OptimizerHook, HOOKS
2
+ try:
3
+ import apex
4
+ except:
5
+ print('apex is not installed')
6
+
7
+
8
+ @HOOKS.register_module()
9
+ class DistOptimizerHook(OptimizerHook):
10
+ """Optimizer hook for distributed training."""
11
+
12
+ def __init__(self, update_interval=1, grad_clip=None, coalesce=True, bucket_size_mb=-1, use_fp16=False):
13
+ self.grad_clip = grad_clip
14
+ self.coalesce = coalesce
15
+ self.bucket_size_mb = bucket_size_mb
16
+ self.update_interval = update_interval
17
+ self.use_fp16 = use_fp16
18
+
19
+ def before_run(self, runner):
20
+ runner.optimizer.zero_grad()
21
+
22
+ def after_train_iter(self, runner):
23
+ runner.outputs['loss'] /= self.update_interval
24
+ if self.use_fp16:
25
+ with apex.amp.scale_loss(runner.outputs['loss'], runner.optimizer) as scaled_loss:
26
+ scaled_loss.backward()
27
+ else:
28
+ runner.outputs['loss'].backward()
29
+ if self.every_n_iters(runner, self.update_interval):
30
+ if self.grad_clip is not None:
31
+ self.clip_grads(runner.model.parameters())
32
+ runner.optimizer.step()
33
+ runner.optimizer.zero_grad()
TransRAC/mmaction/version.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (c) Open-MMLab. All rights reserved.
2
+
3
+ __version__ = '0.15.0'
4
+
5
+
6
+ def parse_version_info(version_str):
7
+ version_info = []
8
+ for x in version_str.split('.'):
9
+ if x.isdigit():
10
+ version_info.append(int(x))
11
+ elif x.find('rc') != -1:
12
+ patch_version = x.split('rc')
13
+ version_info.append(int(patch_version[0]))
14
+ version_info.append(f'rc{patch_version[1]}')
15
+ return tuple(version_info)
16
+
17
+
18
+ version_info = parse_version_info(__version__)
TransRAC/mmcv_custom/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # -*- coding: utf-8 -*-
TransRAC/models/TransRAC.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """TransRAC network"""
2
+ from mmcv import Config
3
+ from mmaction.models import build_model
4
+ from mmcv.runner import load_checkpoint
5
+ import torch
6
+ import torch.nn as nn
7
+ import math
8
+ from torch.cuda.amp import autocast
9
+ import numpy as np
10
+ import torch.nn.functional as F
11
+
12
+
13
+ class attention(nn.Module):
14
+ """Scaled dot-product attention mechanism."""
15
+
16
+ def __init__(self, scale=64, att_dropout=None):
17
+ super().__init__()
18
+ # self.dropout = nn.Dropout(attention_dropout)
19
+ self.softmax = nn.Softmax(dim=-1)
20
+ self.dropout = nn.Dropout(att_dropout)
21
+ self.scale = scale
22
+
23
+ def forward(self, q, k, v, attn_mask=None):
24
+ # q: [B, head, F, model_dim]
25
+ scores = torch.matmul(q, k.transpose(-2, -1)) / math.sqrt(self.scale) # [B,Head, F, F]
26
+ if attn_mask:
27
+ scores = scores.masked_fill_(attn_mask, -np.inf)
28
+ scores = self.softmax(scores)
29
+ scores = self.dropout(scores) # [B,head, F, F]
30
+ # context = torch.matmul(scores, v) # output
31
+ return scores # [B,head,F, F]
32
+
33
+
34
+ class Similarity_matrix(nn.Module):
35
+ ''' buliding similarity matrix by self-attention mechanism '''
36
+
37
+ def __init__(self, num_heads=4, model_dim=512):
38
+ super().__init__()
39
+
40
+ # self.dim_per_head = model_dim // num_heads
41
+ self.num_heads = num_heads
42
+ self.model_dim = model_dim
43
+ self.input_size = 512
44
+ self.linear_q = nn.Linear(self.input_size, model_dim)
45
+ self.linear_k = nn.Linear(self.input_size, model_dim)
46
+ self.linear_v = nn.Linear(self.input_size, model_dim)
47
+
48
+ self.attention = attention(att_dropout=0)
49
+ # self.out = nn.Linear(model_dim, model_dim)
50
+ # self.layer_norm = nn.LayerNorm(model_dim)
51
+
52
+ def forward(self, query, key, value, attn_mask=None):
53
+ batch_size = query.size(0)
54
+ # dim_per_head = self.dim_per_head
55
+ num_heads = self.num_heads
56
+ # linear projection
57
+ query = self.linear_q(query) # [B,F,model_dim]
58
+ key = self.linear_k(key)
59
+ value = self.linear_v(value)
60
+ # split by heads
61
+ # [B,F,model_dim] -> [B,F,num_heads,per_head]->[B,num_heads,F,per_head]
62
+ query = query.reshape(batch_size, -1, num_heads, self.model_dim // self.num_heads).transpose(1, 2)
63
+ key = key.reshape(batch_size, -1, num_heads, self.model_dim // self.num_heads).transpose(1, 2)
64
+ value = value.reshape(batch_size, -1, num_heads, self.model_dim // self.num_heads).transpose(1, 2)
65
+ # similar_matrix :[B,H,F,F ]
66
+ matrix = self.attention(query, key, value, attn_mask)
67
+
68
+ return matrix
69
+
70
+
71
+ class PositionalEncoding(nn.Module):
72
+ def __init__(self, d_model, dropout=0.1, max_len=5000):
73
+ super(PositionalEncoding, self).__init__()
74
+ self.dropout = nn.Dropout(p=dropout)
75
+
76
+ pe = torch.zeros(max_len, d_model)
77
+ position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
78
+ div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
79
+ pe[:, 0::2] = torch.sin(position * div_term)
80
+ pe[:, 1::2] = torch.cos(position * div_term)
81
+ pe = pe.unsqueeze(0).transpose(0, 1)
82
+ self.register_buffer('pe', pe)
83
+
84
+ def forward(self, x):
85
+ x = x + self.pe[:x.size(0), :]
86
+ x = self.dropout(x)
87
+ return x
88
+
89
+
90
+ class TransEncoder(nn.Module):
91
+ '''standard transformer encoder'''
92
+
93
+ def __init__(self, d_model, n_head, dim_ff, dropout=0.0, num_layers=1, num_frames=64):
94
+ super(TransEncoder, self).__init__()
95
+ self.pos_encoder = PositionalEncoding(d_model, 0.1, num_frames)
96
+
97
+ encoder_layer = nn.TransformerEncoderLayer(d_model=d_model,
98
+ nhead=n_head,
99
+ dim_feedforward=dim_ff,
100
+ dropout=dropout,
101
+ activation='relu')
102
+ encoder_norm = nn.LayerNorm(d_model)
103
+ self.trans_encoder = nn.TransformerEncoder(encoder_layer, num_layers, encoder_norm)
104
+
105
+ def forward(self, src):
106
+ src = self.pos_encoder(src)
107
+ e_op = self.trans_encoder(src)
108
+ return e_op
109
+
110
+
111
+ class Prediction(nn.Module):
112
+ ''' predict the density map with densenet '''
113
+
114
+ def __init__(self, input_dim, n_hidden_1, n_hidden_2, out_dim):
115
+ super(Prediction, self).__init__()
116
+ self.layers = nn.Sequential(
117
+ nn.Linear(input_dim, n_hidden_1),
118
+ nn.LayerNorm(n_hidden_1),
119
+ nn.Dropout(p=0.25, inplace=False),
120
+ nn.ReLU(True),
121
+ nn.Linear(n_hidden_1, n_hidden_2),
122
+ nn.ReLU(True),
123
+ nn.Dropout(p=0.25, inplace=False),
124
+ nn.Linear(n_hidden_2, out_dim)
125
+ )
126
+
127
+ def forward(self, x):
128
+ x = self.layers(x)
129
+ return x
130
+
131
+
132
+ class TransferModel(nn.Module):
133
+ def __init__(self, config, checkpoint, num_frames, scales, OPEN=False):
134
+ super(TransferModel, self).__init__()
135
+ self.num_frames = num_frames
136
+ self.config = config
137
+ self.checkpoint = checkpoint
138
+ self.scales = scales
139
+ self.OPEN = OPEN
140
+
141
+ self.backbone = self.load_model() # load pretrain model
142
+
143
+ self.Replication_padding1 = nn.ConstantPad3d((0, 0, 0, 0, 1, 1), 0)
144
+ self.Replication_padding2 = nn.ConstantPad3d((0, 0, 0, 0, 2, 2), 0)
145
+ self.Replication_padding4 = nn.ConstantPad3d((0, 0, 0, 0, 4, 4), 0)
146
+
147
+ self.conv3D = nn.Conv3d(in_channels=768,
148
+ out_channels=512,
149
+ kernel_size=3,
150
+ padding=(3, 1, 1),
151
+ dilation=(3, 1, 1))
152
+
153
+ self.bn1 = nn.BatchNorm3d(512)
154
+ self.SpatialPooling = nn.MaxPool3d(kernel_size=(1, 7, 7))
155
+
156
+ self.sims = Similarity_matrix()
157
+ self.conv3x3 = nn.Conv2d(in_channels=4 * len(self.scales), # num_head*scale_num
158
+ out_channels=32,
159
+ kernel_size=3,
160
+ padding=1)
161
+
162
+ self.bn2 = nn.BatchNorm2d(32)
163
+
164
+ self.dropout1 = nn.Dropout(0.25)
165
+ self.input_projection = nn.Linear(self.num_frames * 32, 512) # 线性投射层
166
+ self.ln1 = nn.LayerNorm(512)
167
+
168
+ self.transEncoder = TransEncoder(d_model=512, n_head=4, dropout=0.2, dim_ff=512, num_layers=1,
169
+ num_frames=self.num_frames)
170
+ self.FC = Prediction(512, 512, 256, 1) #
171
+
172
+ def load_model(self):
173
+ # # # load pretrained model of video swin transformer using mmaction and mmcv API
174
+ cfg = Config.fromfile(self.config)
175
+ model = build_model(cfg.model, train_cfg=cfg.get('train_cfg'), test_cfg=cfg.get('test_cfg'))
176
+
177
+ # # # load hyperparameters by mmcv api
178
+ load_checkpoint(model, self.checkpoint, map_location='cpu')
179
+ backbone = model.backbone
180
+
181
+ # # # load hyperparameters by pytorch
182
+ # loaded_ckpt = torch.load(self.checkpoint)
183
+ # backbone = model.backbone
184
+ # net_dict = backbone.state_dict()
185
+ # state_dict = {k: v for k, v in loaded_ckpt.items() if k in net_dict.keys()}
186
+ # net_dict.update(state_dict)
187
+ # backbone.load_state_dict(net_dict, strict=False)
188
+
189
+ print('--------- backbone loaded ------------')
190
+
191
+ return backbone
192
+
193
+ def forward(self, x):
194
+ # x: tensor([batch_size, channel, temporal_dim, height, width])
195
+ with autocast():
196
+ batch_size, c, num_frames, h, w = x.shape
197
+ # scales = [1,4,8]
198
+ ### We currently only support 1, 4, 8 flames. If you want to add more scale, you can change the part and don't forget padding.
199
+ multi_scales = []
200
+ for scale in self.scales:
201
+ if scale == 4:
202
+ x = self.Replication_padding2(x)
203
+ crops = [x[:, :, i:i + scale, :, :] for i in
204
+ range(0, self.num_frames - scale + scale // 2 * 2, max(scale // 2, 1))]
205
+ elif scale == 8:
206
+ x = self.Replication_padding4(x)
207
+ crops = [x[:, :, i:i + scale, :, :] for i in
208
+ range(0, self.num_frames - scale + scale // 2 * 2, max(scale // 2, 1))]
209
+ else:
210
+ crops = [x[:, :, i:i + 1, :, :] for i in range(0, self.num_frames)]
211
+
212
+ slice = []
213
+ ## feature extract with video SwinTransformer
214
+ if not self.OPEN:
215
+ with torch.no_grad():
216
+ for crop in crops:
217
+ crop = self.backbone(crop) # ->[batch_size, 768, scale/2(up), 7, 7]
218
+ slice.append(crop)
219
+ else: # train the feature extractor (video SwinTransformer backbone)
220
+ for crop in crops:
221
+ crop = self.backbone(crop) # ->[batch_size, 768, scale/2(up), 7, 7]
222
+ slice.append(crop)
223
+
224
+ x_scale = torch.cat(slice, dim=2) # -> [b,768,f,size,size]
225
+ x_scale = F.relu(self.bn1(self.conv3D(x_scale))) # ->[b,512,f,7,7]
226
+ # print(x_scale.shape)
227
+ x_scale = self.SpatialPooling(x_scale) # ->[b,512,f,1,1]
228
+ x_scale = x_scale.squeeze(3).squeeze(3) # -> [b,512,f]
229
+ x_scale = x_scale.transpose(1, 2) # -> [b,f,512]
230
+
231
+ # -------- similarity matrix ---------
232
+ x_sims = F.relu(self.sims(x_scale, x_scale, x_scale)) # -> [b,4,f,f]
233
+ multi_scales.append(x_sims)
234
+
235
+ x = torch.cat(multi_scales, dim=1) # [B,4*scale_num,f,f]
236
+ ## x are the similarity matrixs
237
+ x_matrix = x
238
+ x = F.relu(self.bn2(self.conv3x3(x))) # [b,32,f,f]
239
+ x = self.dropout1(x)
240
+
241
+ x = x.permute(0, 2, 3, 1) # [b,f,f,32]
242
+ # --------- transformer encoder ------
243
+ x = x.flatten(start_dim=2) # ->[b,f,32*f]
244
+ x = F.relu(self.input_projection(x)) # ->[b,f, 512]
245
+ x = self.ln1(x)
246
+
247
+ x = x.transpose(0, 1) # [f,b,512]
248
+ x = self.transEncoder(x) #
249
+ x = x.transpose(0, 1) # ->[b,f, 512]
250
+
251
+ x = self.FC(x) # ->[b,f,1]
252
+ x = x.squeeze(2)
253
+
254
+ return x, x_matrix
TransRAC/models/__init__.py ADDED
File without changes
TransRAC/open_set/new_test.csv ADDED
The diff for this file is too large to render. See raw diff
 
TransRAC/open_set/new_train.csv ADDED
The diff for this file is too large to render. See raw diff
 
TransRAC/open_set/new_valid.csv ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ,Unnamed: 0,type,name,count,L1,L2,L3,L4,L5,L6,L7,L8,L9,L10,L11,L12,L13,L14,L15,L16,L17,L18,L19,L20,L21,L22,L23,L24,L25,L26,L27,L28,L29,L30,L31,L32,L33,L34,L35,L36,L37,L38,L39,L40,L41,L42,L43,L44,L45,L46,L47,L48,L49,L50,L51,L52,L53,L54,L55,L56,L57,L58,L59,L60,L61,L62,L63,L64,L65,L66,L67,L68,L69,L70,L71,L72,L73,L74,L75,L76,L77,L78,L79,L80,L81,L82,L83,L84,L85,L86,L87,L88,L89,L90,L91,L92,L93,L94,L95,L96,L97,L98,L99,L100,L101,L102,L103,L104,L105,L106,L107,L108,L109,L110,L111,L112,L113,L114,L115,L116,L117,L118,L119,L120,L121,L122,L123,L124,L125,L126,L127,L128,L129,L130,L131,L132,L133,L134,L135,L136,L137,L138,L139,L140,L141,L142,L143,L144,L145,L146,L147,L148,L149,L150,L151,L152,L153,L154,L155,L156,L157,L158,L159,L160,L161,L162,L163,L164,L165,L166,L167,L168,L169,L170,L171,L172,L173,L174,L175,L176,L177,L178,L179,L180,L181,L182,L183,L184,L185,L186,L187,L188,L189,L190,L191,L192,L193,L194,L195,L196,L197,L198,L199,L200,L201,L202,L203,L204,L205,L206,L207,L208,L209,L210,L211,L212,L213,L214,L215,L216,L217,L218,L219,L220,L221,L222,L223,L224,L225,L226,L227,L228,L229,L230,L231,L232,L233,L234,L235,L236,L237,L238,L239,L240,L241,L242,L243,L244,L245,L246,L247,L248,L249,L250,L251,L252,L253,L254,L255,L256,L257,L258,L259,L260,L261,L262,L263,L264,L265,L266,L267,L268,L269,L270,L271,L272,L273,L274,L275,L276,L277,L278,L279,L280,L281,L282,L283,L284,L285,L286,L287,L288,L289,L290,L291,L292,L293,L294,L295,L296,L297,L298,L299,L300,L301,L302
2
+ 0,0,frontraise,train951.mp4,4,6,72,72.0,132.0,132.0,204.0,204.0,271.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
3
+ 1,1,frontraise,train952.mp4,10,13,62,62.0,103.0,103.0,126.0,126.0,146.0,146.0,171.0,171.0,194.0,194.0,215.0,215.0,239.0,239.0,260.0,260.0,281.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
4
+ 4,4,front_raise,stu5_11.mp4,6,104,179,179.0,255.0,255.0,330.0,426.0,501.0,703.0,789.0,790.0,853.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
5
+ 7,7,front_raise,stu4_14.mp4,5,6,87,87.0,177.0,177.0,276.0,276.0,377.0,377.0,490.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
6
+ 28,28,frontraise,train940.mp4,5,45,94,94.0,148.0,148.0,201.0,201.0,255.0,255.0,299.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
7
+ 33,33,front_raise,stu9_22.mp4,6,39,124,124.0,207.0,207.0,277.0,566.0,659.0,659.0,750.0,750.0,851.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
8
+ 35,35,front_raise,stu1_14.mp4,3,15,227,227.0,403.0,403.0,540.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
9
+ 39,39,frontraise,train987.mp4,7,0,36,36.0,78.0,78.0,118.0,118.0,160.0,160.0,204.0,204.0,244.0,244.0,279.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
10
+ 42,43,front_raise,stu7_18.mp4,5,40,207,208.0,337.0,338.0,399.0,402.0,466.0,555.0,663.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
11
+ 51,52,front_raise,stu6_8.mp4,6,19,110,111.0,191.0,198.0,346.0,382.0,553.0,579.0,702.0,702.0,822.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
12
+ 54,55,front_raise,stu9_26.mp4,3,142,239,239.0,339.0,339.0,436.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
13
+ 64,66,front_raise,stu2_20.mp4,13,79,145,145.0,198.0,198.0,251.0,251.0,304.0,304.0,360.0,360.0,412.0,412.0,466.0,466.0,518.0,518.0,572.0,572.0,629.0,630.0,679.0,679.0,736.0,736.0,789.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
14
+ 65,67,front_raise,stu10_16.mp4,6,22,94,94.0,147.0,147.0,197.0,197.0,249.0,249.0,300.0,300.0,343.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
15
+ 83,85,frontraise,test586.mp4,7,19,56,56.0,99.0,99.0,136.0,136.0,178.0,178.0,220.0,220.0,257.0,257.0,298.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
16
+ 89,91,front_raise,stu1_20.mp4,3,36,105,105.0,186.0,186.0,265.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
17
+ 93,95,front_raise,stu3_9.mp4,12,24,84,84.0,145.0,145.0,210.0,210.0,278.0,278.0,350.0,350.0,422.0,422.0,492.0,492.0,562.0,562.0,630.0,630.0,708.0,708.0,784.0,784.0,854.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
18
+ 117,119,front_raise,stu6_6.mp4,9,5,309,309.0,455.0,455.0,562.0,563.0,663.0,663.0,770.0,772.0,867.0,867.0,977.0,977.0,1102.0,1107.0,1204.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
19
+ 127,129,front_raise,stu7_21.mp4,7,39,129,129.0,213.0,215.0,314.0,316.0,406.0,407.0,504.0,551.0,606.0,608.0,672.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
20
+ 130,132,front_raise,stu10_23.mp4,9,39,87,87.0,138.0,138.0,188.0,188.0,237.0,365.0,441.0,441.0,489.0,489.0,536.0,536.0,578.0,578.0,615.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
21
+ 138,140,front_raise,stu2_16.mp4,2,336,513,513.0,709.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
22
+ 140,142,front_raise,stu9_25.mp4,1,87,202,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
23
+ 147,149,frontraise,train954.mp4,6,3,47,47.0,91.0,91.0,142.0,142.0,195.0,195.0,243.0,243.0,286.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
24
+ 150,152,front_raise,stu8_17.mp4,23,69,121,121.0,170.0,170.0,219.0,219.0,269.0,269.0,319.0,319.0,369.0,369.0,417.0,417.0,456.0,457.0,505.0,505.0,554.0,554.0,603.0,603.0,646.0,646.0,694.0,694.0,762.0,762.0,803.0,803.0,856.0,856.0,908.0,908.0,951.0,951.0,1006.0,1006.0,1056.0,1056.0,1106.0,1106.0,1157.0,1157.0,1199.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
25
+ 161,163,front_raise,stu10_18.mp4,4,63,457,457.0,724.0,725.0,833.0,833.0,934.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
26
+ 162,164,front_raise,stu6_10.mp4,3,57,167,174.0,298.0,304.0,405.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
27
+ 164,166,frontraise,train1005.mp4,4,1,60,60.0,131.0,131.0,193.0,193.0,273.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
28
+ 167,169,front_raise,stu9_20.mp4,7,64,147,147.0,225.0,225.0,290.0,291.0,376.0,376.0,429.0,429.0,485.0,600.0,712.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
29
+ 173,175,front_raise,stu4_13.mp4,6,4,65,65.0,135.0,135.0,209.0,211.0,286.0,286.0,365.0,366.0,437.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
30
+ 174,176,front_raise,stu5_13.mp4,4,0,60,60.0,129.0,129.0,188.0,188.0,263.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
31
+ 176,178,front_raise,stu10_15.mp4,17,14,73,73.0,127.0,135.0,180.0,180.0,226.0,226.0,268.0,268.0,313.0,313.0,356.0,356.0,404.0,404.0,448.0,448.0,526.0,526.0,569.0,570.0,620.0,620.0,661.0,661.0,710.0,710.0,752.0,752.0,797.0,798.0,844.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
32
+ 195,197,front_raise,stu3_11.mp4,3,17,100,100.0,176.0,176.0,234.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
33
+ 201,203,frontraise,test531.mp4,4,0,79,79.0,151.0,151.0,221.0,222.0,290.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
34
+ 203,205,front_raise,stu2_17.mp4,5,48,147,147.0,298.0,298.0,428.0,428.0,559.0,561.0,695.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
35
+ 204,206,front_raise,stu9_16.mp4,6,46,127,128.0,202.0,202.0,285.0,285.0,361.0,361.0,480.0,480.0,604.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
36
+ 206,208,front_raise,stu1_15.mp4,1,59,215,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
37
+ 207,209,front_raise,stu7_23.mp4,4,0,100,100.0,201.0,203.0,318.0,318.0,418.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
38
+ 219,222,front_raise,stu9_27.mp4,17,6,60,61.0,111.0,198.0,241.0,241.0,279.0,280.0,316.0,316.0,359.0,359.0,399.0,399.0,436.0,437.0,478.0,478.0,516.0,516.0,558.0,559.0,599.0,599.0,636.0,636.0,676.0,676.0,720.0,720.0,759.0,759.0,808.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
39
+ 229,232,frontraise,val359.mp4,1,3,225,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
40
+ 238,241,front_raise,stu5_14.mp4,9,0,75,75.0,157.0,157.0,242.0,242.0,331.0,331.0,411.0,411.0,497.0,497.0,574.0,574.0,654.0,654.0,723.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
41
+ 247,250,front_raise,stu7_15.mp4,4,1,107,108.0,209.0,209.0,318.0,319.0,431.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
42
+ 256,260,frontraise,test558.mp4,2,0,127,128.0,255.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
43
+ 262,266,front_raise,stu4_9.mp4,4,59,254,275.0,407.0,408.0,548.0,549.0,651.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
44
+ 265,269,frontraise,train929.mp4,4,2,49,49.0,108.0,108.0,162.0,162.0,220.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
45
+ 274,279,front_raise,stu8_22.mp4,10,4,50,51.0,94.0,94.0,133.0,133.0,172.0,172.0,211.0,211.0,245.0,329.0,390.0,390.0,436.0,436.0,499.0,499.0,550.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
46
+ 276,281,front_raise,stu1_16.mp4,3,616,711,711.0,763.0,763.0,839.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
47
+ 277,282,front_raise,stu3_17.mp4,5,7,83,83.0,157.0,157.0,222.0,241.0,313.0,313.0,377.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
48
+ 287,292,front_raise,stu4_10.mp4,13,5,42,42.0,92.0,93.0,145.0,145.0,194.0,194.0,243.0,243.0,296.0,297.0,340.0,341.0,387.0,387.0,439.0,440.0,491.0,492.0,534.0,535.0,584.0,586.0,624.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
49
+ 290,295,front_raise,stu2_19.mp4,5,29,148,148.0,272.0,272.0,387.0,387.0,489.0,490.0,595.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
50
+ 293,298,front_raise,stu1_21.mp4,4,190,281,281.0,371.0,371.0,548.0,548.0,719.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
51
+ 311,316,frontraise,test557.mp4,3,24,111,113.0,212.0,213.0,299.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
52
+ 317,322,frontraise,test570.mp4,3,0,95,95.0,177.0,177.0,262.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
53
+ 325,330,front_raise,stu8_18.mp4,9,45,126,127.0,199.0,199.0,275.0,275.0,358.0,358.0,435.0,435.0,504.0,504.0,578.0,578.0,653.0,653.0,719.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
54
+ 338,343,front_raise,stu2_18.mp4,39,59,104,104.0,147.0,147.0,199.0,199.0,241.0,241.0,287.0,287.0,335.0,335.0,379.0,379.0,427.0,427.0,472.0,472.0,517.0,742.0,761.0,761.0,776.0,777.0,791.0,792.0,807.0,807.0,823.0,823.0,838.0,838.0,854.0,854.0,870.0,870.0,887.0,887.0,901.0,1018.0,1036.0,1036.0,1050.0,1050.0,1064.0,1064.0,1078.0,1078.0,1091.0,1092.0,1105.0,1105.0,1120.0,1120.0,1137.0,1137.0,1153.0,1153.0,1165.0,1304.0,1319.0,1319.0,1335.0,1335.0,1349.0,1349.0,1363.0,1363.0,1377.0,1377.0,1392.0,1392.0,1406.0,1406.0,1422.0,1422.0,1434.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
55
+ 342,347,front_raise,stu8_21.mp4,3,45,146,146.0,239.0,239.0,327.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
56
+ 348,353,frontraise,test577.mp4,3,6,80,80.0,160.0,160.0,264.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
57
+ 351,356,front_raise,stu9_14.mp4,11,30,77,77.0,132.0,132.0,174.0,174.0,217.0,217.0,288.0,288.0,337.0,337.0,385.0,463.0,500.0,500.0,536.0,536.0,569.0,569.0,597.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
58
+ 375,380,frontraise,test584.mp4,5,5,39,39.0,79.0,79.0,133.0,133.0,177.0,177.0,215.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
59
+ 378,383,front_raise,stu10_21.mp4,2,66,146,146.0,242.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
60
+ 395,400,front_raise,stu9_23.mp4,5,34,163,195.0,330.0,331.0,446.0,446.0,553.0,566.0,669.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
61
+ 401,406,front_raise,stu10_20.mp4,8,28,212,212.0,327.0,327.0,458.0,458.0,558.0,558.0,647.0,647.0,737.0,737.0,828.0,828.0,913.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
62
+ 403,408,front_raise,stu10_14.mp4,14,49,135,135.0,212.0,212.0,289.0,290.0,363.0,364.0,432.0,432.0,498.0,498.0,564.0,564.0,647.0,647.0,723.0,723.0,805.0,805.0,900.0,900.0,981.0,981.0,1071.0,1071.0,1154.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
63
+ 427,432,frontraise,train1022.mp4,3,52,125,125.0,197.0,197.0,264.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
64
+ 454,459,front_raise,stu6_11.mp4,22,107,127,127.0,146.0,146.0,162.0,162.0,180.0,180.0,198.0,198.0,219.0,219.0,238.0,238.0,264.0,264.0,288.0,288.0,313.0,331.0,410.0,520.0,568.0,568.0,656.0,656.0,733.0,734.0,828.0,828.0,922.0,922.0,964.0,965.0,1002.0,1002.0,1033.0,1033.0,1070.0,1070.0,1114.0,1114.0,1174.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
65
+ 455,460,front_raise,stu6_14.mp4,4,47,112,112.0,190.0,392.0,478.0,478.0,563.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
66
+ 459,465,front_raise,stu3_14.mp4,3,3,136,136.0,199.0,199.0,256.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
67
+ 460,466,frontraise,train941.mp4,4,39,108,108.0,171.0,171.0,229.0,229.0,289.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
68
+ 462,468,frontraise,test541.mp4,3,0,112,112.0,204.0,204.0,299.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
69
+ 469,475,frontraise,train970.mp4,5,40,88,88.0,138.0,138.0,188.0,188.0,236.0,236.0,285.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
70
+ 485,491,front_raise,stu3_8.mp4,6,66,127,127.0,190.0,190.0,257.0,257.0,322.0,322.0,390.0,390.0,448.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
71
+ 489,495,front_raise,stu10_22.mp4,4,96,226,226.0,345.0,345.0,456.0,456.0,554.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
72
+ 492,498,front_raise,stu1_18.mp4,9,40,123,123.0,195.0,195.0,263.0,263.0,336.0,336.0,427.0,427.0,515.0,515.0,611.0,611.0,681.0,681.0,753.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
73
+ 494,500,front_raise,stu2_22.mp4,7,26,56,56.0,95.0,95.0,135.0,135.0,175.0,176.0,215.0,216.0,255.0,255.0,292.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
74
+ 498,504,front_raise,stu2_24.mp4,5,0,124,124.0,290.0,290.0,464.0,465.0,672.0,673.0,869.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
75
+ 500,506,frontraise,train960.mp4,2,1,106,106.0,199.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
76
+ 508,514,front_raise,stu9_24.mp4,2,71,168,168.0,269.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
77
+ 515,521,front_raise,stu6_12.mp4,4,21,97,97.0,177.0,177.0,267.0,267.0,348.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
78
+ 522,528,frontraise,test528.mp4,2,0,168,168.0,281.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
79
+ 525,532,front_raise,stu7_20.mp4,6,27,218,223.0,356.0,357.0,457.0,459.0,530.0,533.0,640.0,640.0,727.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
80
+ 534,541,front_raise,stu6_9.mp4,18,5,100,106.0,210.0,222.0,317.0,334.0,428.0,430.0,512.0,515.0,612.0,615.0,713.0,713.0,798.0,800.0,912.0,912.0,995.0,995.0,1085.0,1085.0,1167.0,1167.0,1267.0,1268.0,1353.0,1353.0,1451.0,1452.0,1555.0,1557.0,1651.0,1654.0,1747.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
81
+ 546,553,front_raise,stu8_20.mp4,8,5,128,128.0,222.0,222.0,331.0,331.0,426.0,426.0,523.0,523.0,633.0,633.0,743.0,743.0,839.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
82
+ 556,563,front_raise,stu7_22.mp4,2,179,278,288.0,473.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
83
+ 559,566,front_raise,stu9_21.mp4,16,349,422,443.0,521.0,522.0,597.0,597.0,667.0,667.0,730.0,730.0,807.0,807.0,876.0,876.0,936.0,937.0,1013.0,1013.0,1098.0,1098.0,1186.0,1187.0,1258.0,1258.0,1337.0,1338.0,1436.0,1617.0,1681.0,1727.0,1792.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
84
+ 565,572,front_raise,stu9_12.mp4,7,0,45,45.0,91.0,91.0,131.0,131.0,176.0,199.0,240.0,240.0,286.0,286.0,334.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
85
+ 569,576,frontraise,test545.mp4,1,0,204,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
86
+ 580,590,frontraise,train1000.mp4,3,49,141,141.0,238.0,238.0,293.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
87
+ 595,605,front_raise,stu5_15.mp4,18,56,109,109.0,177.0,177.0,234.0,234.0,322.0,384.0,463.0,463.0,533.0,533.0,622.0,622.0,707.0,728.0,791.0,791.0,856.0,856.0,927.0,976.0,1040.0,1040.0,1096.0,1096.0,1151.0,1151.0,1192.0,1192.0,1262.0,1262.0,1339.0,1340.0,1420.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
88
+ 610,621,front_raise,stu8_19.mp4,9,45,86,86.0,120.0,120.0,154.0,154.0,187.0,187.0,224.0,224.0,256.0,256.0,291.0,291.0,322.0,322.0,351.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
89
+ 628,639,front_raise,stu9_15.mp4,3,0,184,184.0,362.0,363.0,529.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
90
+ 629,640,frontraise,test587.mp4,3,7,115,115.0,217.0,217.0,299.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
91
+ 635,646,front_raise,stu3_12.mp4,2,155,389,389.0,548.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
92
+ 636,647,front_raise,stu4_8.mp4,3,40,134,137.0,271.0,304.0,424.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
93
+ 12,13,front_raise,stu10_17.mp4,11,6,105,105.0,177.0,177.0,242.0,242.0,317.0,317.0,392.0,392.0,461.0,461.0,541.0,541.0,616.0,616.0,690.0,691.0,769.0,769.0,852.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
94
+ 20,21,front_raise,stu10_24.mp4,14,60,97,97.0,131.0,131.0,162.0,162.0,194.0,243.0,292.0,292.0,326.0,326.0,361.0,361.0,398.0,755.0,791.0,791.0,828.0,828.0,858.0,858.0,892.0,892.0,923.0,923.0,961.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
95
+ 43,44,frontraise,test538.mp4,5,8,64,64.0,122.0,122.0,172.0,172.0,223.0,224.0,268.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
96
+ 49,50,frontraise,test536.mp4,6,21,69,69.0,112.0,112.0,158.0,158.0,203.0,203.0,245.0,245.0,288.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
97
+ 55,56,front_raise,stu5_12.mp4,5,34,152,152.0,255.0,255.0,352.0,352.0,468.0,468.0,582.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
98
+ 56,57,front_raise,stu9_17.mp4,15,45,111,111.0,168.0,192.0,251.0,251.0,320.0,320.0,386.0,386.0,441.0,447.0,511.0,511.0,566.0,566.0,635.0,859.0,932.0,932.0,991.0,991.0,1044.0,1045.0,1098.0,1098.0,1159.0,1159.0,1215.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
99
+ 64,65,front_raise,stu3_10.mp4,5,33,95,95.0,149.0,149.0,199.0,199.0,255.0,255.0,310.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
100
+ 80,81,front_raise,stu4_12.mp4,4,33,158,159.0,261.0,262.0,376.0,376.0,465.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
101
+ 82,83,front_raise,stu7_17.mp4,5,14,100,100.0,195.0,195.0,297.0,299.0,388.0,390.0,467.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
102
+ 99,100,front_raise,stu10_19.mp4,8,24,88,88.0,153.0,153.0,221.0,221.0,287.0,287.0,354.0,354.0,425.0,425.0,495.0,495.0,570.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
103
+ 107,108,frontraise,test534.mp4,3,1,37,37.0,81.0,81.0,130.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
104
+ 109,110,front_raise,stu6_13.mp4,9,28,104,104.0,182.0,182.0,265.0,265.0,329.0,329.0,390.0,390.0,447.0,447.0,509.0,509.0,579.0,579.0,624.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
105
+ 111,112,front_raise,stu8_23.mp4,3,55,133,133.0,194.0,194.0,265.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
106
+ 119,120,frontraise,test539.mp4,2,4,125,125.0,240.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
107
+ 120,121,front_raise,stu2_14.mp4,7,0,40,40.0,89.0,132.0,246.0,246.0,349.0,349.0,441.0,441.0,539.0,539.0,633.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
108
+ 127,128,front_raise,stu3_15.mp4,4,336,486,486.0,555.0,556.0,642.0,642.0,715.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
109
+ 128,129,front_raise,stu3_18.mp4,16,1,40,40.0,77.0,77.0,117.0,117.0,151.0,151.0,187.0,187.0,222.0,380.0,418.0,418.0,453.0,453.0,490.0,490.0,529.0,538.0,590.0,823.0,863.0,863.0,900.0,900.0,936.0,936.0,975.0,975.0,1017.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
110
+ 129,130,front_raise,stu5_16.mp4,10,80,136,136.0,215.0,215.0,284.0,284.0,354.0,354.0,430.0,430.0,499.0,499.0,561.0,561.0,633.0,633.0,713.0,713.0,778.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
111
+ 1,2,front_raise,stu2_15.mp4,8,80,147,147.0,203.0,203.0,264.0,264.0,321.0,321.0,382.0,382.0,443.0,443.0,512.0,512.0,578.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
112
+ 13,14,front_raise,stu2_23.mp4,6,6,113,113.0,221.0,221.0,316.0,316.0,398.0,398.0,495.0,495.0,586.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
113
+ 15,16,front_raise,stu6_7.mp4,5,6,50,50.0,94.0,94.0,130.0,130.0,167.0,167.0,204.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
114
+ 16,17,front_raise,stu9_18.mp4,23,28,72,121.0,166.0,166.0,204.0,339.0,384.0,384.0,423.0,423.0,462.0,463.0,508.0,508.0,554.0,554.0,602.0,649.0,690.0,690.0,730.0,730.0,769.0,769.0,813.0,841.0,889.0,889.0,935.0,1031.0,1073.0,1073.0,1122.0,1122.0,1169.0,1169.0,1213.0,1246.0,1336.0,1336.0,1423.0,1423.0,1517.0,1517.0,1606.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
115
+ 18,19,front_raise,stu7_19.mp4,4,15,314,318.0,512.0,515.0,688.0,690.0,853.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
116
+ 19,20,front_raise,stu7_16.mp4,4,0,62,63.0,114.0,114.0,163.0,163.0,211.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
117
+ 23,24,front_raise,stu5_17.mp4,4,0,51,51.0,136.0,136.0,215.0,215.0,289.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
118
+ 26,27,front_raise,stu2_21.mp4,4,8,112,112.0,237.0,237.0,376.0,376.0,511.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
119
+ 40,41,front_raise,stu1_17.mp4,6,5,39,39.0,85.0,85.0,129.0,129.0,174.0,222.0,264.0,264.0,318.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
120
+ 43,44,frontraise,train1025.mp4,3,0,102,102.0,214.0,214.0,299.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
121
+ 57,58,frontraise,test571.mp4,3,0,94,94.0,190.0,191.0,283.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
122
+ 68,69,front_raise,stu3_16.mp4,5,32,157,270.0,464.0,464.0,583.0,583.0,691.0,692.0,830.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
123
+ 70,71,front_raise,stu1_19.mp4,3,89,176,202.0,280.0,280.0,348.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
124
+ 77,78,front_raise,stu4_11.mp4,4,25,141,143.0,241.0,243.0,343.0,345.0,447.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
125
+ 82,83,front_raise,stu9_13.mp4,2,60,138,363.0,445.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
126
+ 93,96,front_raise,stu10_13.mp4,3,11,314,314.0,509.0,509.0,719.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
127
+ 94,98,front_raise,stu9_19.mp4,7,34,119,119.0,191.0,191.0,273.0,273.0,364.0,364.0,436.0,436.0,542.0,542.0,632.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
128
+ 101,105,frontraise,train990.mp4,4,87,133,134.0,175.0,176.0,224.0,226.0,271.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
129
+ 103,107,frontraise,train1010.mp4,6,25,68,68.0,115.0,115.0,158.0,158.0,200.0,200.0,241.0,241.0,281.0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,