├── .gitignore
├── .gitmodules
├── .idea
├── misc.xml
├── modules.xml
├── other.xml
├── pseudo-3d.iml
├── vcs.xml
└── workspace.xml
├── Dataset.py
├── README.md
├── data
├── .DS_Store
├── breakfastTrainTestList
│ ├── classInd.txt
│ ├── fixclass.py
│ ├── testlist01.txt
│ ├── trainlist01.txt
│ └── validationlist01.txt
├── extract.py
├── makeVideoFolder.py
├── merlTrainTestList
│ ├── classInd.txt
│ ├── merl.py
│ ├── testlist01.txt
│ ├── trainlist01.txt
│ └── validationlist01.txt
├── movefile.py
└── ucfTrainTestlist
│ ├── .DS_Store
│ ├── classInd.txt
│ ├── testlist01.txt
│ ├── testlist02.txt
│ ├── testlist03.txt
│ ├── trainlist01.txt
│ ├── trainlist02.txt
│ ├── trainlist03.txt
│ ├── validationlist01.txt
│ ├── validationlist02.txt
│ └── validationlist03.txt
├── dog.0.jpg
├── logger.py
├── lr_scheduler.py
├── main.py
├── meter.py
├── models
├── C3D.py
├── i3d.py
├── i3dpt.py
└── p3d_model.py
├── test.py
├── train.py
├── transforms.py
├── utils.py
├── video_transforms.py
└── visualize.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.tar
2 | .DS_Store
3 | __pycache__
4 | lib/
5 | Breakfast/
6 | UCF101/
7 | *.pickle
8 | *.pth
9 | *.tar
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naviocean/pseudo-3d-pytorch/50297d11248630792709782f467982e80c281384/.gitmodules
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/other.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/pseudo-3d.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
155 |
156 |
157 |
158 | num_frames
159 | frames
160 | RandomResizedCrop
161 | name_list
162 | C3D
163 | clip = self.get_video_tensor(frames_path)
164 | validate
165 | data =
166 | normalize
167 | Stack
168 | roll
169 | mean
170 | momentum
171 | np.
172 | data
173 | 160
174 | Nor
175 | Normalize
176 | self.data_folder
177 | criterion
178 | 216
179 | get_optim_policies
180 | 16
181 | pretrained
182 | check_gpu
183 | transfer_model
184 | trans
185 | self.ac
186 | transfer
187 | RandomSizedCrop
188 |
189 |
190 | check_gpu()
191 |
192 |
193 | $PROJECT_DIR$/videotransforms
194 | $PROJECT_DIR$
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 | true
238 | DEFINITION_ORDER
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 | Python
253 |
254 |
255 |
256 |
257 | PyCompatibilityInspection
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 | 1534148020669
331 |
332 |
333 | 1534148020669
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
--------------------------------------------------------------------------------
/Dataset.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 | import os
3 | import os.path
4 | import glob
5 | import numpy as np
6 | from torch.utils.data import Dataset
7 | import random
8 |
9 | class MyDataset(Dataset):
10 | def __init__(self, root_path, data_folder='train', name_list='ucfTrainTestlist', version=1, transform=None, num_frames=16, modality='RGB', random=False):
11 | self.root_path = root_path
12 | self.num_frames = num_frames
13 | self.data_folder = data_folder
14 | self.random = random
15 | self.split_file = os.path.join(self.root_path, name_list,
16 | str(data_folder) + 'list0' + str(version) + '.txt')
17 | self.label_file = os.path.join(self.root_path, name_list, 'classInd.txt')
18 | self.label_dict = self.get_labels()
19 |
20 | self.video_dict = self.get_video_list()
21 |
22 | self.version = version
23 | self.transform = transform
24 |
25 | def get_video_list(self):
26 | res = []
27 | with open(self.split_file) as fin:
28 | for line in list(fin):
29 | line = line.replace("\n", "")
30 | split = line.split(" ")
31 | # get number frames of each video
32 | video_path = split[0].split('.')[0]
33 | frames_path = os.path.join(self.root_path, self.data_folder, video_path)
34 | allfiles = glob.glob(frames_path + '/*.jpg')
35 | # remove video which has < 16 image frames
36 | if len(allfiles) >= self.num_frames:
37 | res.append(split[0])
38 | return res
39 |
40 | # Get all labels from classInd.txt
41 | def get_labels(self):
42 | label_dict = {}
43 | with open(self.label_file) as fin:
44 | for row in list(fin):
45 | row = row.replace("\n", "").split(" ")
46 | # -1 because the index of array is start from 0
47 | label_dict[row[1]] = int(row[0]) - 1
48 | return label_dict
49 |
50 | # Get all frame images of video
51 | def get_all_images(self, dir, file_ext="jpg", sort_files=True):
52 | allfiles = glob.glob(dir + '/*.' + file_ext)
53 | if sort_files and len(allfiles) > 0:
54 | allfiles = sorted(allfiles)
55 | return allfiles
56 |
57 | def get_video_tensor(self, dir):
58 | images = self.get_all_images(dir)
59 | # print(dir)
60 | # print(len(images))
61 | seed = np.random.random_integers(0, len(images) - self.num_frames) # random sampling
62 | clip = list()
63 | if self.random:
64 | orders = list(range(len(images)))
65 | random_picked = random.sample(orders, self.num_frames)
66 | for i in range(self.num_frames):
67 | idx = random_picked[i]
68 | img = Image.open(images[idx])
69 | clip.append(img)
70 | else:
71 | for i in range(self.num_frames):
72 | img = Image.open(images[i + seed])
73 | clip.append(img)
74 | clip = self.transform(clip)
75 | return clip
76 |
77 | # stuff
78 | def __getitem__(self, index):
79 | video = self.video_dict[index]
80 | video_path = video.split('.')[0]
81 |
82 | frames_path = os.path.join(self.root_path, self.data_folder, video_path)
83 | clip = self.get_video_tensor(frames_path)
84 | # get label name from video path
85 | label_name = video_path.split('/')[0]
86 | label_index = self.label_dict[label_name];
87 | return (clip, label_index)
88 |
89 | def __len__(self):
90 | return len(self.video_dict)
91 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Pseudo-3D Residual Networks
3 |
4 | This repo implements the network structure of P3D[1] with PyTorch, pre-trained model weights are converted from caffemodel, which is supported from the [author's repo](https://github.com/ZhaofanQiu/pseudo-3d-residual-networks)
5 |
6 |
7 | ### Requirements:
8 |
9 | - pytorch
10 | - numpy
11 | - ffmpeg (for extract image frames from videos)
12 |
13 | ### Pretrained weights
14 |
15 | 1, P3D-199 trained on Kinetics dataset:
16 |
17 | [Google Drive url](https://drive.google.com/drive/folders/1u_l-yvhS0shpW6e0tCiqPE7Bd1qQZKdD)
18 |
19 | 2, P3D-199 trianed on Kinetics Optical Flow (TVL1):
20 |
21 | [Google Drive url](https://drive.google.com/drive/folders/1u_l-yvhS0shpW6e0tCiqPE7Bd1qQZKdD)
22 |
23 |
24 | ### Prepare Dataset UCF101
25 | First, download the dataset from UCF into the data folder and then extract it.
26 | ```
27 | cd data && wget http://crcv.ucf.edu/data/UCF101/UCF101.rar
28 | unrar e UCF101.rar
29 | ```
30 |
31 | Next, make 3 folders train, test and validation:
32 | ```
33 | mkdir train test validation
34 | ```
35 | Finally, run scripts to extract image frames from videos;
36 | ```
37 | python move.py
38 | python makeVideoFolder.py
39 | python extract.py
40 | ```
41 |
42 | ### Run Code
43 | 1, For Training from scratch
44 | ```
45 | python main.py /path/data/
46 | ```
47 | 2, For Fine-tuning
48 | ```
49 | python main.py /path/data/ --pretrained
50 | ```
51 | 3, For Evaluate model
52 | ```
53 | python main.py /path/data/ --resume=checkpoint.pth.tar --evaluate
54 | ```
55 | 4, For testing model
56 | ```
57 | python main.py /path/data/ --test
58 | ```
59 |
60 | ### Experiment Result From Us
61 | Dataset | Accuracy
62 | ---|---|
63 | UCF-101 | 81.6%
64 | MERL Shopping | 82.6%
65 |
66 | Reference:
67 |
68 | [1][Learning Spatio-Temporal Representation with Pseudo-3D Residual,ICCV2017](http://openaccess.thecvf.com/content_iccv_2017/html/Qiu_Learning_Spatio-Temporal_Representation_ICCV_2017_paper.html)
69 |
--------------------------------------------------------------------------------
/data/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naviocean/pseudo-3d-pytorch/50297d11248630792709782f467982e80c281384/data/.DS_Store
--------------------------------------------------------------------------------
/data/breakfastTrainTestList/classInd.txt:
--------------------------------------------------------------------------------
1 | 1 AddSaltnpepper
2 | 2 AddTeabag
3 | 3 ButterPan
4 | 4 CrackEgg
5 | 5 CutBun
6 | 6 CutFruit
7 | 7 CutOrange
8 | 8 FryEgg
9 | 9 FryPancake
10 | 10 PeelFruit
11 | 11 PourCereals
12 | 12 PourCoffee
13 | 13 PourDough2pan
14 | 14 PourJuice
15 | 15 PourMilk
16 | 16 PourOil
17 | 17 PourWater
18 | 18 PutEgg2plate
19 | 19 PutFruit2bowl
20 | 20 PutToppingontop
21 | 21 SmearButter
22 | 22 SpoonPowder
23 | 23 SqueezeOrange
24 | 24 StirDough
25 | 25 StirMilk
26 | 26 TakeCup
27 | 27 TakePlate
28 |
--------------------------------------------------------------------------------
/data/breakfastTrainTestList/fixclass.py:
--------------------------------------------------------------------------------
1 | from random import shuffle
2 |
3 |
4 | class FixClass(object):
5 | class_list = []
6 | train_file = './trainlist01.txt'
7 | class_file = './classInd.txt'
8 |
9 | def __init__(self):
10 | super(FixClass, self).__init__()
11 | # self.readFile()
12 | # self.writeClassFile()
13 | self.shuffeFile()
14 |
15 | def readFile(self):
16 | with open(self.train_file) as fin:
17 | for row in list(fin):
18 | class_name = row.strip().split('/')[0]
19 | if class_name not in self.class_list:
20 | self.class_list.append(class_name)
21 | self.class_list.sort()
22 | print(len(self.class_list))
23 |
24 | def writeClassFile(self):
25 | # earse content file
26 | with open(self.class_file, 'w'): pass
27 | # write content file
28 | with open(self.class_file, 'a') as f:
29 | for i, key in enumerate(self.class_list):
30 | line = str(i + 1) + ' ' + key + '\n';
31 | f.write(line)
32 | f.close
33 | print('write class file done')
34 |
35 | def shuffeFile(self):
36 | files = ['./trainlist01.txt','testlist01.txt','validationlist01.txt']
37 |
38 | for i in range(len(files)):
39 | file = files[i]
40 | with open(file) as fin:
41 | rows = [row.strip() for row in list(fin)]
42 |
43 | shuffle(rows)
44 | # earse content file
45 | with open(file, 'w'): pass
46 | # write content file
47 | with open(file, 'a') as f:
48 | for row in rows:
49 | line = row + '\n';
50 | f.write(line)
51 | f.close
52 | print('write file done')
53 |
54 |
55 | FixClass()
56 |
--------------------------------------------------------------------------------
/data/breakfastTrainTestList/testlist01.txt:
--------------------------------------------------------------------------------
1 | PourMilk/P32_pancake_996_PourMilk.avi
2 | CutOrange/P32_juice_86_CutOrange.avi
3 | CutFruit/P35_salad_642_CutFruit.avi
4 | PutFruit2bowl/P25_salad_2496_PutFruit2bowl.avi
5 | SqueezeOrange/P35_juice_156_SqueezeOrange.avi
6 | PourMilk/P03_cereal_428_PourMilk.avi
7 | CutFruit/P35_salad_187_CutFruit.avi
8 | PutToppingontop/P27_sandwich_1003_PutToppingontop.avi
9 | PutFruit2bowl/P25_salad_1576_PutFruit2bowl.avi
10 | PutToppingontop/P28_sandwich_1899_PutToppingontop.avi
11 | TakePlate/P40_salad_26_TakePlate.avi
12 | FryEgg/P29_friedegg_279_FryEgg.avi
13 | CutFruit/P31_salad_1029_CutFruit.avi
14 | StirMilk/P33_milk_453_StirMilk.avi
15 | PutFruit2bowl/P27_salad_1936_PutFruit2bowl.avi
16 | PourJuice/P36_juice_1216_PourJuice.avi
17 | PeelFruit/P31_salad_1184_PeelFruit.avi
18 | CutBun/P34_sandwich_41_CutBun.avi
19 | PeelFruit/P27_salad_61_PeelFruit.avi
20 | CutFruit/P32_salad_1863_CutFruit.avi
21 | PutEgg2plate/P31_scrambledegg_4304_PutEgg2plate.avi
22 | PutEgg2plate/P29_scrambledegg_2493_PutEgg2plate.avi
23 | SmearButter/P28_sandwich_649_SmearButter.avi
24 | PourMilk/P28_cereal_212_PourMilk.avi
25 | SmearButter/P35_sandwich_348_SmearButter.avi
26 | AddSaltnpepper/P35_scrambledegg_285_AddSaltnpepper.avi
27 | PourCoffee/P25_coffee_1_PourCoffee.avi
28 | StirMilk/P40_milk_602_StirMilk.avi
29 | PourMilk/P34_milk_436_PourMilk.avi
30 | CutOrange/P35_juice_86_CutOrange.avi
31 | CutFruit/P33_salad_3725_CutFruit.avi
32 | PutFruit2bowl/P35_salad_572_PutFruit2bowl.avi
33 | TakePlate/P40_scrambledegg_1542_TakePlate.avi
34 | SqueezeOrange/P29_juice_314_SqueezeOrange.avi
35 | CutFruit/P27_salad_1496_CutFruit.avi
36 | TakePlate/P27_scrambledegg_2879_TakePlate.avi
37 | PeelFruit/P25_salad_146_PeelFruit.avi
38 | PutEgg2plate/P36_friedegg_3766_PutEgg2plate.avi
39 | FryPancake/P35_pancake_1238_FryPancake.avi
40 | FryEgg/P25_scrambledegg_1568_FryEgg.avi
41 | CutFruit/P27_salad_3866_CutFruit.avi
42 | AddTeabag/P35_tea_50_AddTeabag.avi
43 | PeelFruit/P40_salad_1026_PeelFruit.avi
44 | CutFruit/P32_salad_2183_CutFruit.avi
45 | SmearButter/P34_sandwich_371_SmearButter.avi
46 | PourMilk/P35_milk_331_PourMilk.avi
47 | ButterPan/P33_friedegg_35_ButterPan.avi
48 | FryEgg/P36_scrambledegg_952_FryEgg.avi
49 | CutOrange/P27_juice_71_CutOrange.avi
50 | SpoonPowder/P29_milk_262_SpoonPowder.avi
51 | PourWater/P27_tea_566_PourWater.avi
52 | PourWater/P32_tea_260_PourWater.avi
53 | PutFruit2bowl/P31_salad_1429_PutFruit2bowl.avi
54 | PourMilk/P30_coffee_277_PourMilk.avi
55 | PutToppingontop/P34_sandwich_581_PutToppingontop.avi
56 | CrackEgg/P29_friedegg_10_CrackEgg.avi
57 | SqueezeOrange/P40_juice_212_SqueezeOrange.avi
58 | FryEgg/P33_friedegg_1086_FryEgg.avi
59 | CutBun/P27_sandwich_69_CutBun.avi
60 | AddTeabag/P31_tea_244_AddTeabag.avi
61 | PeelFruit/P31_salad_454_PeelFruit.avi
62 | PourOil/P37_pancake_1155_PourOil.avi
63 | PourCoffee/P34_coffee_56_PourCoffee.avi
64 | StirDough/P40_pancake_785_StirDough.avi
65 | SpoonPowder/P35_milk_171_SpoonPowder.avi
66 | PourMilk/P37_cereal_299_PourMilk.avi
67 | PutEgg2plate/P33_friedegg_2501_PutEgg2plate.avi
68 | FryEgg/P30_scrambledegg_1663_FryEgg.avi
69 | CutBun/P10_sandwich_120_CutBun.avi
70 | TakeCup/P03_milk_54_TakeCup.avi
71 | PourCereals/P29_cereal_59_PourCereals.avi
72 | CrackEgg/P35_friedegg_159_CrackEgg.avi
73 | SpoonPowder/P31_milk_657_SpoonPowder.avi
74 | PutEgg2plate/P35_scrambledegg_1875_PutEgg2plate.avi
75 | TakePlate/P37_friedegg_1823_TakePlate.avi
76 | PourMilk/P33_pancake_912_PourMilk.avi
77 | PourWater/P29_tea_370_PourWater.avi
78 | PutFruit2bowl/P31_salad_4917_PutFruit2bowl.avi
79 | CrackEgg/P32_scrambledegg_2_CrackEgg.avi
80 | PutFruit2bowl/P36_salad_5056_PutFruit2bowl.avi
81 | CutBun/P35_sandwich_187_CutBun.avi
82 | FryPancake/P36_pancake_2843_FryPancake.avi
83 | PutFruit2bowl/P37_salad_1420_PutFruit2bowl.avi
84 | PeelFruit/P25_salad_1116_PeelFruit.avi
85 | PutToppingontop/P03_sandwich_2780_PutToppingontop.avi
86 | PourDough2pan/P32_pancake_2131_PourDough2pan.avi
87 | FryPancake/P29_pancake_3711_FryPancake.avi
88 | CrackEgg/P29_scrambledegg_66_CrackEgg.avi
89 | FryEgg/P33_scrambledegg_2655_FryEgg.avi
90 | CrackEgg/P31_pancake_1_CrackEgg.avi
91 | StirDough/P31_pancake_1356_StirDough.avi
92 | AddSaltnpepper/P37_scrambledegg_438_AddSaltnpepper.avi
93 | CrackEgg/P27_friedegg_270_CrackEgg.avi
94 | AddSaltnpepper/P32_scrambledegg_449_AddSaltnpepper.avi
95 | PourMilk/P32_milk_359_PourMilk.avi
96 | PourMilk/P29_milk_42_PourMilk.avi
97 | PourMilk/P03_coffee_378_PourMilk.avi
98 | PourOil/P27_scrambledegg_1030_PourOil.avi
99 | PourDough2pan/P37_pancake_1342_PourDough2pan.avi
100 | PourMilk/P33_milk_314_PourMilk.avi
101 | CutFruit/P29_salad_786_CutFruit.avi
102 | PutFruit2bowl/P25_salad_2156_PutFruit2bowl.avi
103 | ButterPan/P31_scrambledegg_435_ButterPan.avi
104 | SqueezeOrange/P31_juice_401_SqueezeOrange.avi
105 | CutFruit/P35_salad_1197_CutFruit.avi
106 | PourCoffee/P37_coffee_1_PourCoffee.avi
107 | TakeCup/P31_coffee_1_TakeCup.avi
108 | TakeCup/P03_tea_89_TakeCup.avi
109 | CrackEgg/P36_scrambledegg_310_CrackEgg.avi
110 | TakePlate/P32_friedegg_2046_TakePlate.avi
111 | CrackEgg/P34_scrambledegg_1_CrackEgg.avi
112 | CutFruit/P03_salad_853_CutFruit.avi
113 | AddSaltnpepper/P32_friedegg_2534_AddSaltnpepper.avi
114 | StirMilk/P35_milk_421_StirMilk.avi
115 | CrackEgg/P27_pancake_1_CrackEgg.avi
116 | FryEgg/P25_friedegg_970_FryEgg.avi
117 | PourMilk/P35_cereal_331_PourMilk.avi
118 | PourMilk/P34_coffee_195_PourMilk.avi
119 | PeelFruit/P03_salad_3310_PeelFruit.avi
120 | PourCereals/P37_cereal_39_PourCereals.avi
121 | FryEgg/P33_scrambledegg_525_FryEgg.avi
122 | PutFruit2bowl/P32_salad_2323_PutFruit2bowl.avi
123 | PutEgg2plate/P37_scrambledegg_1089_PutEgg2plate.avi
124 | PourCereals/P33_cereal_82_PourCereals.avi
125 | CutFruit/P28_salad_1615_CutFruit.avi
126 | PourJuice/P25_juice_459_PourJuice.avi
127 | PutFruit2bowl/P29_salad_2466_PutFruit2bowl.avi
128 | PeelFruit/P28_salad_1775_PeelFruit.avi
129 | AddSaltnpepper/P32_friedegg_1357_AddSaltnpepper.avi
130 | CutFruit/P25_salad_936_CutFruit.avi
131 | StirDough/P27_pancake_657_StirDough.avi
132 | PourOil/P36_friedegg_1_PourOil.avi
133 | SpoonPowder/P30_milk_21_SpoonPowder.avi
134 | ButterPan/P30_scrambledegg_40_ButterPan.avi
135 | CutFruit/P37_salad_2700_CutFruit.avi
136 | CrackEgg/P36_pancake_278_CrackEgg.avi
137 | AddSaltnpepper/P27_scrambledegg_184_AddSaltnpepper.avi
138 | SmearButter/P10_sandwich_455_SmearButter.avi
139 | PourDough2pan/P30_pancake_2601_PourDough2pan.avi
140 | FryEgg/P37_scrambledegg_681_FryEgg.avi
141 | AddTeabag/P36_tea_11_AddTeabag.avi
142 | PourOil/P34_friedegg_50_PourOil.avi
143 | StirMilk/P36_milk_569_StirMilk.avi
144 | CutFruit/P28_salad_10_CutFruit.avi
145 | PourJuice/P27_juice_1656_PourJuice.avi
146 |
--------------------------------------------------------------------------------
/data/breakfastTrainTestList/trainlist01.txt:
--------------------------------------------------------------------------------
1 | PutEgg2plate/P34_scrambledegg_3778_PutEgg2plate.avi
2 | SqueezeOrange/P28_juice_530_SqueezeOrange.avi
3 | PutToppingontop/P37_sandwich_986_PutToppingontop.avi
4 | FryPancake/P40_pancake_1764_FryPancake.avi
5 | PutFruit2bowl/P27_salad_4516_PutFruit2bowl.avi
6 | CrackEgg/P25_friedegg_629_CrackEgg.avi
7 | CutFruit/P37_salad_20_CutFruit.avi
8 | CrackEgg/P25_scrambledegg_437_CrackEgg.avi
9 | PeelFruit/P31_salad_4142_PeelFruit.avi
10 | TakePlate/P35_scrambledegg_1505_TakePlate.avi
11 | PourOil/P36_pancake_1957_PourOil.avi
12 | PeelFruit/P31_salad_3607_PeelFruit.avi
13 | TakeCup/P31_tea_34_TakeCup.avi
14 | PutEgg2plate/P40_scrambledegg_1633_PutEgg2plate.avi
15 | SpoonPowder/P32_milk_214_SpoonPowder.avi
16 | CutFruit/P25_salad_2196_CutFruit.avi
17 | PutEgg2plate/P30_scrambledegg_2488_PutEgg2plate.avi
18 | CutFruit/P32_salad_2033_CutFruit.avi
19 | StirDough/P27_pancake_1642_StirDough.avi
20 | FryEgg/P31_friedegg_1868_FryEgg.avi
21 | PourMilk/P32_milk_124_PourMilk.avi
22 | CutFruit/P31_salad_4762_CutFruit.avi
23 | TakePlate/P30_pancake_5210_TakePlate.avi
24 | PourMilk/P40_pancake_631_PourMilk.avi
25 | PutFruit2bowl/P03_salad_4110_PutFruit2bowl.avi
26 | PourMilk/P37_pancake_459_PourMilk.avi
27 | ButterPan/P32_pancake_1926_ButterPan.avi
28 | SqueezeOrange/P33_juice_272_SqueezeOrange.avi
29 | StirMilk/P34_milk_801_StirMilk.avi
30 | TakeCup/P33_coffee_83_TakeCup.avi
31 | AddSaltnpepper/P37_friedegg_335_AddSaltnpepper.avi
32 | PourWater/P36_tea_81_PourWater.avi
33 | AddSaltnpepper/P34_scrambledegg_184_AddSaltnpepper.avi
34 | AddSaltnpepper/P27_friedegg_2053_AddSaltnpepper.avi
35 | PourMilk/P25_milk_787_PourMilk.avi
36 | CutBun/P37_sandwich_11_CutBun.avi
37 | CutOrange/P37_juice_61_CutOrange.avi
38 | PourCoffee/P03_coffee_199_PourCoffee.avi
39 | CrackEgg/P40_pancake_1_CrackEgg.avi
40 | SqueezeOrange/P25_juice_133_SqueezeOrange.avi
41 | CrackEgg/P32_pancake_1_CrackEgg.avi
42 | CutFruit/P03_salad_548_CutFruit.avi
43 | SqueezeOrange/P37_juice_221_SqueezeOrange.avi
44 | TakePlate/P29_scrambledegg_2363_TakePlate.avi
45 | PourMilk/P27_milk_70_PourMilk.avi
46 | ButterPan/P31_pancake_3216_ButterPan.avi
47 | PourJuice/P35_juice_736_PourJuice.avi
48 | FryEgg/P37_friedegg_626_FryEgg.avi
49 | CutFruit/P25_salad_1446_CutFruit.avi
50 | SqueezeOrange/P32_juice_241_SqueezeOrange.avi
51 | PutFruit2bowl/P27_salad_3816_PutFruit2bowl.avi
52 | SpoonPowder/P31_milk_1907_SpoonPowder.avi
53 | PutFruit2bowl/P32_salad_3278_PutFruit2bowl.avi
54 | PourOil/P36_scrambledegg_3_PourOil.avi
55 | PutFruit2bowl/P27_salad_4046_PutFruit2bowl.avi
56 | PutFruit2bowl/P28_salad_1265_PutFruit2bowl.avi
57 | PourCereals/P32_cereal_123_PourCereals.avi
58 | CutFruit/P27_salad_2501_CutFruit.avi
59 | FryPancake/P32_pancake_2582_FryPancake.avi
60 | PourOil/P03_friedegg_385_PourOil.avi
61 | PourMilk/P29_pancake_1068_PourMilk.avi
62 | PourCereals/P40_cereal_56_PourCereals.avi
63 | StirMilk/P03_milk_754_StirMilk.avi
64 | FryPancake/P30_pancake_3076_FryPancake.avi
65 | CutFruit/P32_salad_2853_CutFruit.avi
66 | AddTeabag/P32_tea_120_AddTeabag.avi
67 | CutOrange/P33_juice_52_CutOrange.avi
68 | CutFruit/P28_salad_520_CutFruit.avi
69 | PourOil/P35_scrambledegg_668_PourOil.avi
70 | CrackEgg/P33_friedegg_329_CrackEgg.avi
71 | CutBun/P33_sandwich_201_CutBun.avi
72 | StirDough/P30_pancake_1382_StirDough.avi
73 | FryEgg/P30_scrambledegg_1988_FryEgg.avi
74 | PutFruit2bowl/P31_salad_2334_PutFruit2bowl.avi
75 | CutFruit/P30_salad_1582_CutFruit.avi
76 | CutFruit/P33_salad_4230_CutFruit.avi
77 | PourCoffee/P32_coffee_46_PourCoffee.avi
78 | PutEgg2plate/P35_friedegg_2520_PutEgg2plate.avi
79 | SpoonPowder/P36_milk_40_SpoonPowder.avi
80 | AddSaltnpepper/P30_scrambledegg_1533_AddSaltnpepper.avi
81 | PeelFruit/P29_salad_1616_PeelFruit.avi
82 | StirMilk/P27_milk_580_StirMilk.avi
83 | PutFruit2bowl/P40_salad_2071_PutFruit2bowl.avi
84 | SpoonPowder/P03_milk_229_SpoonPowder.avi
85 | StirDough/P35_pancake_543_StirDough.avi
86 | CrackEgg/P32_friedegg_829_CrackEgg.avi
87 | FryEgg/P40_friedegg_743_FryEgg.avi
88 | FryEgg/P34_friedegg_647_FryEgg.avi
89 | FryEgg/P36_friedegg_840_FryEgg.avi
90 | CrackEgg/P30_scrambledegg_640_CrackEgg.avi
91 | CutFruit/P33_salad_30_CutFruit.avi
92 | CutFruit/P29_salad_2151_CutFruit.avi
93 | PourMilk/P27_pancake_1504_PourMilk.avi
94 | SmearButter/P03_sandwich_652_SmearButter.avi
95 | PourJuice/P30_juice_867_PourJuice.avi
96 | CrackEgg/P37_pancake_1_CrackEgg.avi
97 | PutFruit2bowl/P37_salad_3240_PutFruit2bowl.avi
98 | PeelFruit/P28_salad_140_PeelFruit.avi
99 | PutToppingontop/P25_sandwich_1045_PutToppingontop.avi
100 | CutFruit/P31_salad_1749_CutFruit.avi
101 | PourJuice/P33_juice_812_PourJuice.avi
102 | PutFruit2bowl/P31_salad_3557_PutFruit2bowl.avi
103 | PourDough2pan/P35_pancake_1037_PourDough2pan.avi
104 | CrackEgg/P37_friedegg_126_CrackEgg.avi
105 | SpoonPowder/P25_milk_382_SpoonPowder.avi
106 | PeelFruit/P30_salad_1203_PeelFruit.avi
107 | CutOrange/P36_juice_21_CutOrange.avi
108 | PeelFruit/P33_salad_3960_PeelFruit.avi
109 | PutFruit2bowl/P03_salad_2633_PutFruit2bowl.avi
110 | PourCereals/P28_cereal_44_PourCereals.avi
111 | PeelFruit/P31_salad_4967_PeelFruit.avi
112 | PutToppingontop/P03_sandwich_1763_PutToppingontop.avi
113 | CrackEgg/P03_friedegg_599_CrackEgg.avi
114 | CutBun/P40_sandwich_86_CutBun.avi
115 | PutFruit2bowl/P31_salad_1154_PutFruit2bowl.avi
116 | CutFruit/P31_salad_5297_CutFruit.avi
117 | SmearButter/P29_sandwich_559_SmearButter.avi
118 | PourOil/P25_friedegg_8_PourOil.avi
119 | FryEgg/P37_scrambledegg_143_FryEgg.avi
120 | AddTeabag/P25_tea_156_AddTeabag.avi
121 | TakePlate/P32_juice_36_TakePlate.avi
122 | CutFruit/P32_salad_218_CutFruit.avi
123 | PourCereals/P35_cereal_169_PourCereals.avi
124 | PeelFruit/P31_salad_2377_PeelFruit.avi
125 | PutEgg2plate/P27_scrambledegg_2986_PutEgg2plate.avi
126 | ButterPan/P33_pancake_1487_ButterPan.avi
127 | SqueezeOrange/P36_juice_286_SqueezeOrange.avi
128 | PourWater/P30_tea_127_PourWater.avi
129 | SmearButter/P33_sandwich_396_SmearButter.avi
130 | PutFruit2bowl/P27_salad_2401_PutFruit2bowl.avi
131 | PourWater/P31_tea_424_PourWater.avi
132 | PutEgg2plate/P32_friedegg_3468_PutEgg2plate.avi
133 | CutFruit/P33_salad_3185_CutFruit.avi
134 | FryEgg/P27_friedegg_525_FryEgg.avi
135 | PourMilk/P34_cereal_297_PourMilk.avi
136 | TakePlate/P27_friedegg_2256_TakePlate.avi
137 | CutFruit/P03_salad_2960_CutFruit.avi
138 | SmearButter/P36_sandwich_405_SmearButter.avi
139 | PutToppingontop/P31_sandwich_2077_PutToppingontop.avi
140 | PourCoffee/P33_coffee_121_PourCoffee.avi
141 | TakePlate/P32_pancake_6005_TakePlate.avi
142 | CrackEgg/P31_friedegg_475_CrackEgg.avi
143 | PutEgg2plate/P33_scrambledegg_2969_PutEgg2plate.avi
144 | FryEgg/P30_scrambledegg_1316_FryEgg.avi
145 | CutBun/P36_sandwich_15_CutBun.avi
146 | CutFruit/P31_salad_3937_CutFruit.avi
147 | PutEgg2plate/P03_friedegg_4007_PutEgg2plate.avi
148 | SpoonPowder/P33_milk_65_SpoonPowder.avi
149 | PeelFruit/P35_salad_857_PeelFruit.avi
150 | CutFruit/P40_salad_1636_CutFruit.avi
151 | PutEgg2plate/P36_scrambledegg_1838_PutEgg2plate.avi
152 | CutFruit/P03_salad_3740_CutFruit.avi
153 | CutFruit/P32_salad_2368_CutFruit.avi
154 | PourJuice/P30_juice_522_PourJuice.avi
155 | PourMilk/P30_pancake_1211_PourMilk.avi
156 | FryPancake/P30_pancake_5634_FryPancake.avi
157 | ButterPan/P40_friedegg_22_ButterPan.avi
158 | PourMilk/P33_coffee_381_PourMilk.avi
159 | PeelFruit/P29_salad_1036_PeelFruit.avi
160 | SqueezeOrange/P30_juice_592_SqueezeOrange.avi
161 | PourMilk/P31_pancake_1039_PourMilk.avi
162 | FryPancake/P27_pancake_2784_FryPancake.avi
163 | CutFruit/P31_salad_2104_CutFruit.avi
164 | PutFruit2bowl/P35_salad_827_PutFruit2bowl.avi
165 | StirDough/P32_pancake_1119_StirDough.avi
166 | PutToppingontop/P40_sandwich_1006_PutToppingontop.avi
167 | PutEgg2plate/P34_friedegg_2323_PutEgg2plate.avi
168 | AddTeabag/P27_tea_331_AddTeabag.avi
169 | PutEgg2plate/P27_friedegg_2656_PutEgg2plate.avi
170 | TakeCup/P40_milk_7_TakeCup.avi
171 | PourCoffee/P31_coffee_161_PourCoffee.avi
172 | SqueezeOrange/P27_juice_226_SqueezeOrange.avi
173 | PourJuice/P29_juice_1173_PourJuice.avi
174 | PutFruit2bowl/P32_salad_2778_PutFruit2bowl.avi
175 | FryEgg/P03_friedegg_1382_FryEgg.avi
176 | PourCereals/P25_cereal_304_PourCereals.avi
177 | ButterPan/P32_pancake_758_ButterPan.avi
178 | FryPancake/P37_pancake_1608_FryPancake.avi
179 | StirMilk/P29_milk_451_StirMilk.avi
180 | SqueezeOrange/P30_juice_145_SqueezeOrange.avi
181 | PourCereals/P27_cereal_49_PourCereals.avi
182 | SpoonPowder/P34_milk_221_SpoonPowder.avi
183 | FryEgg/P35_scrambledegg_1589_FryEgg.avi
184 | PourCereals/P36_cereal_32_PourCereals.avi
185 | PourMilk/P37_coffee_714_PourMilk.avi
186 | PutEgg2plate/P25_friedegg_1742_PutEgg2plate.avi
187 | CutBun/P31_sandwich_157_CutBun.avi
188 | ButterPan/P32_friedegg_291_ButterPan.avi
189 | PourOil/P35_friedegg_49_PourOil.avi
190 | PeelFruit/P27_salad_3021_PeelFruit.avi
191 | CutFruit/P36_salad_4011_CutFruit.avi
192 | PourMilk/P35_coffee_203_PourMilk.avi
193 | CrackEgg/P33_scrambledegg_163_CrackEgg.avi
194 | TakePlate/P29_pancake_6866_TakePlate.avi
195 | CutBun/P29_sandwich_280_CutBun.avi
196 | PourWater/P37_tea_21_PourWater.avi
197 | StirDough/P30_pancake_2366_StirDough.avi
198 | SmearButter/P31_sandwich_577_SmearButter.avi
199 | FryPancake/P31_pancake_4511_FryPancake.avi
200 | PourCereals/P31_cereal_95_PourCereals.avi
201 | PourCoffee/P30_coffee_76_PourCoffee.avi
202 | CutOrange/P30_juice_20_CutOrange.avi
203 | AddTeabag/P33_tea_260_AddTeabag.avi
204 | PeelFruit/P37_salad_1600_PeelFruit.avi
205 | FryEgg/P34_scrambledegg_1486_FryEgg.avi
206 | AddSaltnpepper/P25_friedegg_804_AddSaltnpepper.avi
207 | TakePlate/P27_pancake_5820_TakePlate.avi
208 | TakeCup/P03_coffee_38_TakeCup.avi
209 | PourMilk/P32_cereal_278_PourMilk.avi
210 | SmearButter/P40_sandwich_376_SmearButter.avi
211 | PourMilk/P29_cereal_241_PourMilk.avi
212 | PutFruit2bowl/P32_salad_2153_PutFruit2bowl.avi
213 | CutBun/P28_sandwich_384_CutBun.avi
214 | TakePlate/P31_pancake_8433_TakePlate.avi
215 | AddTeabag/P29_tea_50_AddTeabag.avi
216 | CrackEgg/P34_friedegg_228_CrackEgg.avi
217 | TakePlate/P30_scrambledegg_2329_TakePlate.avi
218 | PourMilk/P30_pancake_2285_PourMilk.avi
219 | PourWater/P34_tea_417_PourWater.avi
220 | CrackEgg/P30_pancake_1_CrackEgg.avi
221 | CutFruit/P33_salad_1240_CutFruit.avi
222 | PourDough2pan/P33_pancake_1879_PourDough2pan.avi
223 | PutToppingontop/P33_sandwich_856_PutToppingontop.avi
224 | AddSaltnpepper/P36_friedegg_418_AddSaltnpepper.avi
225 | SmearButter/P32_sandwich_221_SmearButter.avi
226 | PourMilk/P33_cereal_213_PourMilk.avi
227 | CrackEgg/P36_friedegg_94_CrackEgg.avi
228 | SmearButter/P27_sandwich_288_SmearButter.avi
229 | PutToppingontop/P32_sandwich_796_PutToppingontop.avi
230 | PutEgg2plate/P40_friedegg_2104_PutEgg2plate.avi
231 | CutFruit/P36_salad_181_CutFruit.avi
232 | PourDough2pan/P31_pancake_3604_PourDough2pan.avi
233 | PeelFruit/P33_salad_3405_PeelFruit.avi
234 | StirMilk/P32_milk_324_StirMilk.avi
235 | PourMilk/P40_milk_72_PourMilk.avi
236 | PourWater/P03_tea_919_PourWater.avi
237 | StirDough/P29_pancake_1229_StirDough.avi
238 | PutFruit2bowl/P35_salad_1362_PutFruit2bowl.avi
239 | AddSaltnpepper/P30_scrambledegg_1832_AddSaltnpepper.avi
240 | StirMilk/P37_milk_551_StirMilk.avi
241 | PourOil/P27_friedegg_24_PourOil.avi
242 | PourMilk/P31_milk_972_PourMilk.avi
243 | PourJuice/P34_juice_630_PourJuice.avi
244 | FryEgg/P31_scrambledegg_1655_FryEgg.avi
245 | CutFruit/P27_salad_3681_CutFruit.avi
246 | AddSaltnpepper/P33_scrambledegg_2220_AddSaltnpepper.avi
247 | CrackEgg/P27_scrambledegg_11_CrackEgg.avi
248 | PourMilk/P31_coffee_441_PourMilk.avi
249 | PourMilk/P40_cereal_308_PourMilk.avi
250 | CrackEgg/P37_scrambledegg_1_CrackEgg.avi
251 | CutOrange/P28_juice_140_CutOrange.avi
252 | PutFruit2bowl/P32_salad_1813_PutFruit2bowl.avi
253 | FryEgg/P32_scrambledegg_1491_FryEgg.avi
254 | PourOil/P34_scrambledegg_1082_PourOil.avi
255 | PourMilk/P35_pancake_413_PourMilk.avi
256 | CrackEgg/P31_scrambledegg_1450_CrackEgg.avi
257 | PourDough2pan/P40_pancake_1604_PourDough2pan.avi
258 | PutToppingontop/P10_sandwich_875_PutToppingontop.avi
259 | CutFruit/P29_salad_1206_CutFruit.avi
260 | PutFruit2bowl/P40_salad_931_PutFruit2bowl.avi
261 | PourMilk/P36_pancake_547_PourMilk.avi
262 | AddTeabag/P34_tea_176_AddTeabag.avi
263 | PeelFruit/P27_salad_1416_PeelFruit.avi
264 | TakeCup/P30_coffee_1_TakeCup.avi
265 | SmearButter/P37_sandwich_161_SmearButter.avi
266 | ButterPan/P29_pancake_2464_ButterPan.avi
267 | FryEgg/P31_scrambledegg_2409_FryEgg.avi
268 | PutFruit2bowl/P29_salad_1466_PutFruit2bowl.avi
269 | SmearButter/P25_sandwich_265_SmearButter.avi
270 | PutEgg2plate/P29_friedegg_3435_PutEgg2plate.avi
271 | PourMilk/P36_cereal_354_PourMilk.avi
272 | PeelFruit/P33_salad_180_PeelFruit.avi
273 | PourMilk/P36_milk_209_PourMilk.avi
274 | PeelFruit/P33_salad_2020_PeelFruit.avi
275 | CutFruit/P25_salad_1626_CutFruit.avi
276 | StirMilk/P31_milk_1292_StirMilk.avi
277 | TakeCup/P33_tea_30_TakeCup.avi
278 | PeelFruit/P31_salad_1464_PeelFruit.avi
279 | PourJuice/P32_juice_1151_PourJuice.avi
280 | PeelFruit/P31_salad_1884_PeelFruit.avi
281 | TakePlate/P40_sandwich_291_TakePlate.avi
282 | CutFruit/P25_salad_71_CutFruit.avi
283 | PourMilk/P30_milk_211_PourMilk.avi
284 | PourJuice/P37_juice_1390_PourJuice.avi
285 | SpoonPowder/P40_milk_302_SpoonPowder.avi
286 | PourMilk/P27_coffee_555_PourMilk.avi
287 | CutOrange/P31_juice_21_CutOrange.avi
288 | AddSaltnpepper/P31_scrambledegg_2319_AddSaltnpepper.avi
289 | CrackEgg/P29_pancake_1_CrackEgg.avi
290 | CutBun/P25_sandwich_65_CutBun.avi
291 | CutOrange/P40_juice_46_CutOrange.avi
292 | FryEgg/P29_scrambledegg_734_FryEgg.avi
293 | CrackEgg/P35_pancake_1_CrackEgg.avi
294 | PutToppingontop/P36_sandwich_1015_PutToppingontop.avi
295 | AddSaltnpepper/P35_friedegg_457_AddSaltnpepper.avi
296 | PutToppingontop/P10_sandwich_1550_PutToppingontop.avi
297 | StirDough/P33_pancake_1052_StirDough.avi
298 | CrackEgg/P33_pancake_348_CrackEgg.avi
299 | StirDough/P30_pancake_1938_StirDough.avi
300 | PourCoffee/P35_coffee_68_PourCoffee.avi
301 | AddTeabag/P30_tea_22_AddTeabag.avi
302 | FryEgg/P40_friedegg_1642_FryEgg.avi
303 | PutFruit2bowl/P31_salad_1854_PutFruit2bowl.avi
304 | PutFruit2bowl/P32_salad_1993_PutFruit2bowl.avi
305 | AddTeabag/P03_tea_239_AddTeabag.avi
306 | ButterPan/P32_scrambledegg_738_ButterPan.avi
307 | StirMilk/P31_milk_2107_StirMilk.avi
308 | PourMilk/P03_milk_514_PourMilk.avi
309 | PourJuice/P28_juice_1910_PourJuice.avi
310 | PourDough2pan/P36_pancake_2242_PourDough2pan.avi
311 | PourCereals/P03_cereal_150_PourCereals.avi
312 | PutEgg2plate/P37_friedegg_1868_PutEgg2plate.avi
313 | PourWater/P35_tea_285_PourWater.avi
314 | AddSaltnpepper/P34_friedegg_360_AddSaltnpepper.avi
315 | CutBun/P32_sandwich_21_CutBun.avi
316 | PourMilk/P25_cereal_484_PourMilk.avi
317 | StirDough/P37_pancake_575_StirDough.avi
318 | AddSaltnpepper/P36_scrambledegg_655_AddSaltnpepper.avi
319 | PutToppingontop/P35_sandwich_566_PutToppingontop.avi
320 | PourMilk/P27_cereal_449_PourMilk.avi
321 | CutFruit/P27_salad_2066_CutFruit.avi
322 | ButterPan/P31_friedegg_66_ButterPan.avi
323 | CutOrange/P29_juice_83_CutOrange.avi
324 | PourWater/P25_tea_886_PourWater.avi
325 | PourOil/P37_friedegg_25_PourOil.avi
326 | PeelFruit/P36_salad_1741_PeelFruit.avi
327 | TakeCup/P25_milk_232_TakeCup.avi
328 | PutFruit2bowl/P31_salad_5407_PutFruit2bowl.avi
329 | PutFruit2bowl/P28_salad_3945_PutFruit2bowl.avi
330 | FryEgg/P27_scrambledegg_1922_FryEgg.avi
331 | PourDough2pan/P27_pancake_2178_PourDough2pan.avi
332 | CutFruit/P40_salad_81_CutFruit.avi
333 | SpoonPowder/P27_milk_251_SpoonPowder.avi
334 | StirMilk/P30_milk_351_StirMilk.avi
335 | PourCoffee/P27_coffee_115_PourCoffee.avi
336 | CutFruit/P27_salad_1341_CutFruit.avi
337 |
--------------------------------------------------------------------------------
/data/breakfastTrainTestList/validationlist01.txt:
--------------------------------------------------------------------------------
1 | PourMilk/P31_cereal_347_PourMilk.avi
2 | AddTeabag/P37_tea_201_AddTeabag.avi
3 | AddSaltnpepper/P40_scrambledegg_399_AddSaltnpepper.avi
4 | PourDough2pan/P29_pancake_3379_PourDough2pan.avi
5 | PourCoffee/P29_coffee_1_PourCoffee.avi
6 | StirMilk/P25_milk_912_StirMilk.avi
7 | CrackEgg/P40_friedegg_504_CrackEgg.avi
8 | PeelFruit/P29_salad_31_PeelFruit.avi
9 | PutFruit2bowl/P31_salad_4102_PutFruit2bowl.avi
10 | CutOrange/P34_juice_75_CutOrange.avi
11 | SqueezeOrange/P34_juice_187_SqueezeOrange.avi
12 | CutFruit/P25_salad_746_CutFruit.avi
13 | CutFruit/P27_salad_4096_CutFruit.avi
14 | TakePlate/P31_scrambledegg_4090_TakePlate.avi
15 | CutFruit/P31_salad_1289_CutFruit.avi
16 | PourMilk/P37_cereal_357_PourMilk.avi
17 | CrackEgg/P35_scrambledegg_1_CrackEgg.avi
18 | PutFruit2bowl/P25_salad_886_PutFruit2bowl.avi
19 | PeelFruit/P30_salad_258_PeelFruit.avi
20 | FryEgg/P35_scrambledegg_1029_FryEgg.avi
21 | CrackEgg/P40_scrambledegg_1_CrackEgg.avi
22 | CutBun/P03_sandwich_260_CutBun.avi
23 | PourMilk/P29_coffee_316_PourMilk.avi
24 | AddSaltnpepper/P33_friedegg_768_AddSaltnpepper.avi
25 | CutFruit/P28_salad_3135_CutFruit.avi
26 | FryPancake/P33_pancake_2102_FryPancake.avi
27 | PourMilk/P37_milk_306_PourMilk.avi
28 | SmearButter/P10_sandwich_1145_SmearButter.avi
29 | ButterPan/P30_pancake_1640_ButterPan.avi
30 | PutEgg2plate/P31_friedegg_6459_PutEgg2plate.avi
31 | FryEgg/P35_friedegg_686_FryEgg.avi
32 | CutFruit/P30_salad_596_CutFruit.avi
33 | FryEgg/P40_scrambledegg_932_FryEgg.avi
34 | PourWater/P33_tea_100_PourWater.avi
35 | PutEgg2plate/P32_scrambledegg_2624_PutEgg2plate.avi
36 | SpoonPowder/P37_milk_21_SpoonPowder.avi
37 | PutToppingontop/P29_sandwich_1618_PutToppingontop.avi
38 | TakeCup/P34_tea_81_TakeCup.avi
39 | PutFruit2bowl/P36_salad_1571_PutFruit2bowl.avi
40 | StirDough/P36_pancake_1043_StirDough.avi
41 | PourOil/P35_pancake_961_PourOil.avi
42 | PourCereals/P34_cereal_63_PourCereals.avi
43 | PourJuice/P31_juice_1406_PourJuice.avi
44 |
--------------------------------------------------------------------------------
/data/extract.py:
--------------------------------------------------------------------------------
1 | """
2 | After moving all the files using the 1_ file, we run this one to extract
3 | the images from the videos and also create a data file we can use
4 | for training and testing later.
5 | """
6 | import csv
7 | import glob
8 | import os
9 | import os.path
10 | from subprocess import call
11 |
12 |
13 | def extract_files():
14 | """After we have all of our videos split between train and test, and
15 | all nested within folders representing their classes, we need to
16 | make a data file that we can reference when training our RNN(s).
17 | This will let us keep track of image sequences and other parts
18 | of the training process.
19 |
20 | We'll first need to extract images from each of the videos. We'll
21 | need to record the following data in the file:
22 |
23 | [train|test], class, filename, nb frames
24 |
25 | Extracting can be done with ffmpeg:
26 | `ffmpeg -i video.mpg image-%04d.jpg`
27 | """
28 | data_file = []
29 | folders = ['./train/', './test/', './validation/']
30 |
31 | for folder in folders:
32 | class_folders = glob.glob(folder + '*')
33 | for class_folder in class_folders:
34 | label_folders = glob.glob(class_folder + '/*')
35 | for vid_class in label_folders:
36 | class_files = glob.glob(vid_class + '/*.avi')
37 |
38 | for video_path in class_files:
39 | # Get the parts of the file.
40 | video_parts = get_video_parts(video_path)
41 |
42 | train_or_test, classname, videofoldername, filename_no_ext, filename = video_parts
43 |
44 | # Only extract if we haven't done it yet. Otherwise, just get
45 | # the info.
46 | if not check_already_extracted(video_parts):
47 | # Now extract it.
48 | src = train_or_test + '/' + classname + '/' + videofoldername + '/' +\
49 | filename
50 | dest = train_or_test + '/' + classname + '/' + videofoldername + '/' +\
51 | filename_no_ext + '-%04d.jpg'
52 | call(["ffmpeg", "-i", src, dest])
53 |
54 | # Now get how many frames it is.
55 | nb_frames = get_nb_frames_for_video(video_parts)
56 |
57 | data_file.append(
58 | [train_or_test, classname, filename_no_ext, nb_frames])
59 |
60 | print("Generated %d frames for %s" %
61 | (nb_frames, filename_no_ext))
62 |
63 | with open('data_file.csv', 'w') as fout:
64 | writer = csv.writer(fout)
65 | writer.writerows(data_file)
66 |
67 | print("Extracted and wrote %d video files." % (len(data_file)))
68 |
69 |
70 | def get_nb_frames_for_video(video_parts):
71 | """Given video parts of an (assumed) already extracted video, return
72 | the number of frames that were extracted."""
73 | train_or_test, classname, videofoldername, filename_no_ext, _ = video_parts
74 | generated_files = glob.glob(train_or_test + '/' + classname + '/' + videofoldername + '/' +
75 | filename_no_ext + '*.jpg')
76 | return len(generated_files)
77 |
78 |
79 | def get_video_parts(video_path):
80 | """Given a full path to a video, return its parts."""
81 | parts = video_path.split('/')
82 | filename = parts[4]
83 | filename_no_ext = filename.split('.')[0]
84 | videofoldername = parts[3]
85 | classname = parts[2]
86 | train_or_test = parts[1]
87 |
88 | return train_or_test, classname, videofoldername, filename_no_ext, filename
89 |
90 |
91 | def check_already_extracted(video_parts):
92 | """Check to see if we created the -0001 frame of this file."""
93 | train_or_test, classname, videofoldername, filename_no_ext, _ = video_parts
94 | return bool(os.path.exists(train_or_test + '/' + classname + '/' + videofoldername +
95 | '/' + filename_no_ext + '-0001.jpg'))
96 |
97 |
98 | def main():
99 | """
100 | Extract images from videos and build a new file that we
101 | can use as our data input file. It can have format:
102 |
103 | [train|test], class, filename, nb frames
104 | """
105 | extract_files()
106 |
107 | if __name__ == '__main__':
108 | main()
109 |
--------------------------------------------------------------------------------
/data/makeVideoFolder.py:
--------------------------------------------------------------------------------
1 | import glob
2 | import os
3 | import os.path
4 | import sys
5 | import shutil
6 |
7 |
8 | class MakeVideoFolder(object):
9 | """docstring for MakeVideoFolder"""
10 | folders = ['./train/', './test/', './validation/']
11 |
12 | def __init__(self):
13 | super(MakeVideoFolder, self).__init__()
14 | self.scanFolder()
15 |
16 | def scanFolder(self):
17 | for folder in self.folders:
18 | class_folders = glob.glob(folder + '*')
19 | for vid_class in class_folders:
20 | class_files = glob.glob(vid_class + '/*.*')
21 | for video_path in class_files:
22 | classname, filename_no_ext, filename = self.get_video_parts(
23 | video_path)
24 | folder_name = filename_no_ext.split('-')[0]
25 | destFolder = vid_class + '/' + folder_name
26 | self.checkAndMakeFolder(destFolder)
27 |
28 | shutil.move(video_path, destFolder + '/' + filename)
29 | print('move file %s' % (filename_no_ext))
30 |
31 | def checkAndMakeFolder(self, folderPath):
32 | try:
33 | os.stat(folderPath)
34 | except:
35 | os.mkdir(folderPath)
36 |
37 | def get_video_parts(self, video_path):
38 | """Given a full path to a video, return its parts."""
39 | parts = video_path.split('/')
40 | filename = parts[3]
41 | filename_no_ext = filename.split('.')[0]
42 | classname = parts[2]
43 | return classname, filename_no_ext, filename
44 |
45 |
46 | MakeVideoFolder()
47 |
--------------------------------------------------------------------------------
/data/merlTrainTestList/classInd.txt:
--------------------------------------------------------------------------------
1 | 1 ReachToShelf
2 | 2 RetractFromShelf
3 | 3 HandInShelf
4 | 4 InspectProduct
5 | 5 InspectShelf
6 |
--------------------------------------------------------------------------------
/data/merlTrainTestList/merl.py:
--------------------------------------------------------------------------------
1 | import scipy.io
2 | import random
3 | import math
4 | import sys
5 | import glob
6 | import os
7 | import os.path
8 | from subprocess import call
9 |
10 |
11 | class MERL_Shopping(object):
12 | classFile = './merlTrainTestList/classInd.txt'
13 | testFile = './merlTrainTestList/testlist01.txt'
14 | valFile = './merlTrainTestList/validationlist01.txt'
15 | trainFile = './merlTrainTestList/trainlist01.txt'
16 | inputVids = './Videos_MERL_Shopping_Dataset'
17 | outputVids = './output'
18 | labelFolder = './Labels_MERL_Shopping_Dataset'
19 | categories = dict()
20 | labels = ['ReachToShelf', 'RetractFromShelf',
21 | 'HandInShelf', 'InspectProduct', 'InspectShelf']
22 | framerate = 30
23 |
24 | def __init__(self):
25 | self.checkDataFolder()
26 |
27 | self.checkOutputFolder()
28 | self.scanVideoFiles()
29 | self.pickRandom()
30 |
31 | def checkOutputFolder(self):
32 | try:
33 | os.stat(self.outputVids)
34 | except:
35 | os.mkdir(self.outputVids)
36 |
37 | def checkDataFolder(self):
38 | self.checkAndMakeFolder(folderPath='./merlTrainTestList')
39 |
40 | def checkAndMakeFolder(self, folderPath):
41 | try:
42 | os.stat(folderPath)
43 | except:
44 | os.mkdir(folderPath)
45 |
46 | def writeClassFile(self):
47 | # earse content file
48 | with open(self.classFile, 'w'):
49 | pass
50 | # write content file
51 | with open(self.classFile, 'a') as f:
52 | for i, key in enumerate(self.categories.keys()):
53 | line = str(i + 1) + ' ' + key + '\n'
54 | f.write(line)
55 | f.close
56 | print('write class file done')
57 |
58 | def writeOutputFile(self, list, file):
59 | # earse content file
60 | with open(file, 'w'):
61 | pass
62 | # write content file
63 | with open(file, 'a') as f:
64 | for filepath in list:
65 | filename = os.path.basename(filepath)
66 | cat = filename.split('.')[0]
67 | cat = cat.split('_')
68 | cat = cat[len(cat) - 1]
69 | line = cat + '/' + filename + '\n'
70 | f.write(line)
71 | f.close
72 | print('write file done')
73 |
74 | def checkFileExisted(self, filepath):
75 | return bool(os.path.exists(filepath))
76 |
77 | def checkAlreadySplit(self):
78 | return
79 |
80 | def spiltVideoByLabel(self, labelFilePath, src):
81 | # read matlab file
82 | mat = scipy.io.loadmat(labelFilePath)
83 | filename = os.path.basename(src)
84 | for i in range(len(mat['tlabs'])):
85 | label = self.labels[i]
86 | frames = mat['tlabs'][i][0]
87 | for i in range(len(frames)):
88 | # get start frame
89 | start_frame = int(frames[i][0])
90 | start_time = start_frame / self.framerate
91 | # get end frame
92 | end_frame = int(frames[i][1])
93 | end_time = (end_frame - start_frame) / self.framerate
94 |
95 | folder = self.outputVids + '/' + label
96 | dest_name = filename.split(
97 | '.')[0] + '_' + str(start_frame) + '_' + str(end_frame) + '_' + label + '.mp4'
98 |
99 | dest = folder + '/' + dest_name
100 |
101 | # check folder existed or not
102 | self.checkAndMakeFolder(folder)
103 | if not self.checkFileExisted(filepath=dest):
104 | call(["ffmpeg", '-i', src, '-ss',
105 | str(start_time), '-t', str(end_time), dest])
106 | print("Splited %s" % (src))
107 | print(start_frame, end_frame, start_time, end_time, dest)
108 |
109 | if label in self.categories:
110 | self.categories[label].append(
111 | label + '/' + dest_name)
112 | else:
113 | self.categories[label] = [
114 | label + '/' + dest_name]
115 |
116 | # get all video files
117 | # read coarse file and then split video to multiparts with label
118 | def scanVideoFiles(self):
119 | for root, dirs, files in os.walk(self.inputVids):
120 | for file in files:
121 | if file.endswith(".mp4"):
122 | mp4FilePath = root + '/' + file
123 | filename = file.split('.')[0]
124 | filename = filename.replace('_crop', '_label')
125 | # generate coarse file path
126 | labelFilePath = self.labelFolder + '/' + filename + '.mat'
127 | # check coarse file existed or not?
128 | if self.checkFileExisted(filepath=labelFilePath):
129 | # read coarse file to get label
130 | self.spiltVideoByLabel(
131 | labelFilePath, src=mp4FilePath)
132 | else:
133 | print('not found %s %s' % (root, mp4FilePath))
134 |
135 | def diff(self, li1, li2):
136 | li_dif = [i for i in li1 + li2 if i not in li1 or i not in li2]
137 | return li_dif
138 |
139 | def getRandomList(self, dataList):
140 | if dataList != []:
141 | elem = random.choice(dataList)
142 | dataList.remove(elem)
143 | else:
144 | elem = None
145 | # print(dataList)
146 | return dataList, elem
147 |
148 | def getNumRandomList(self, datalist, num):
149 | newlist = []
150 | for x in range(0, num):
151 | datalist, item = self.getRandomList(datalist)
152 | newlist.append(item)
153 | return datalist, newlist
154 |
155 | def pickRandom(self):
156 | val_list = []
157 | test_list = []
158 | train_list = []
159 | # print("%s" % (self.categories))
160 | cats = self.categories
161 | for key in cats.keys():
162 | total = len(cats[key])
163 | if total >= 10:
164 | val_total = math.trunc(total * (1 / 10))
165 | if val_total == 0:
166 | val_total = 1
167 | test_total = math.trunc(total * (3 / 10))
168 | if test_total == 0:
169 | test_total = 1
170 | train_total = total - val_total - test_total
171 | cats[key], tmp_list = self.getNumRandomList(
172 | cats[key], val_total)
173 |
174 | val_list = val_list + tmp_list
175 |
176 | cats[key], tmp_list = self.getNumRandomList(
177 | cats[key], test_total)
178 | test_list = test_list + tmp_list
179 | train_list = train_list + cats[key]
180 | print(total, val_total, test_total, train_total)
181 | # print(test_list)
182 | if len(val_list) > 0:
183 | self.writeOutputFile(val_list, self.valFile)
184 | if len(test_list) > 0:
185 | self.writeOutputFile(test_list, self.testFile)
186 | if len(train_list) > 0:
187 | self.writeOutputFile(train_list, self.trainFile)
188 |
189 | self.writeClassFile()
190 |
191 |
192 | MERL_Shopping()
193 |
--------------------------------------------------------------------------------
/data/merlTrainTestList/validationlist01.txt:
--------------------------------------------------------------------------------
1 | ReachToShelf/16_3_crop_1719_1746_ReachToShelf.mp4
2 | ReachToShelf/10_3_crop_3966_3997_ReachToShelf.mp4
3 | ReachToShelf/11_1_crop_2177_2203_ReachToShelf.mp4
4 | ReachToShelf/7_2_crop_1967_1999_ReachToShelf.mp4
5 | ReachToShelf/17_2_crop_2647_2669_ReachToShelf.mp4
6 | ReachToShelf/17_2_crop_1577_1605_ReachToShelf.mp4
7 | ReachToShelf/37_1_crop_3266_3301_ReachToShelf.mp4
8 | ReachToShelf/40_1_crop_526_561_ReachToShelf.mp4
9 | ReachToShelf/7_3_crop_1672_1707_ReachToShelf.mp4
10 | ReachToShelf/1_1_crop_376_406_ReachToShelf.mp4
11 | ReachToShelf/13_2_crop_4061_4144_ReachToShelf.mp4
12 | ReachToShelf/9_1_crop_605_636_ReachToShelf.mp4
13 | ReachToShelf/19_1_crop_2410_2430_ReachToShelf.mp4
14 | ReachToShelf/4_2_crop_1036_1084_ReachToShelf.mp4
15 | ReachToShelf/17_1_crop_2034_2055_ReachToShelf.mp4
16 | ReachToShelf/16_1_crop_2479_2497_ReachToShelf.mp4
17 | ReachToShelf/19_2_crop_2682_2710_ReachToShelf.mp4
18 | ReachToShelf/27_2_crop_1876_1911_ReachToShelf.mp4
19 | ReachToShelf/17_2_crop_2563_2575_ReachToShelf.mp4
20 | ReachToShelf/36_1_crop_1546_1576_ReachToShelf.mp4
21 | ReachToShelf/33_1_crop_3105_3140_ReachToShelf.mp4
22 | ReachToShelf/13_3_crop_607_639_ReachToShelf.mp4
23 | ReachToShelf/1_1_crop_156_183_ReachToShelf.mp4
24 | ReachToShelf/32_3_crop_1019_1035_ReachToShelf.mp4
25 | ReachToShelf/4_2_crop_3487_3513_ReachToShelf.mp4
26 | ReachToShelf/16_3_crop_2616_2650_ReachToShelf.mp4
27 | ReachToShelf/26_3_crop_947_973_ReachToShelf.mp4
28 | ReachToShelf/8_1_crop_271_297_ReachToShelf.mp4
29 | ReachToShelf/1_1_crop_3377_3414_ReachToShelf.mp4
30 | ReachToShelf/2_3_crop_2343_2384_ReachToShelf.mp4
31 | ReachToShelf/23_2_crop_4669_4694_ReachToShelf.mp4
32 | ReachToShelf/15_2_crop_230_260_ReachToShelf.mp4
33 | ReachToShelf/37_1_crop_200_243_ReachToShelf.mp4
34 | ReachToShelf/10_3_crop_480_506_ReachToShelf.mp4
35 | ReachToShelf/12_1_crop_193_224_ReachToShelf.mp4
36 | ReachToShelf/8_1_crop_2733_2773_ReachToShelf.mp4
37 | ReachToShelf/2_3_crop_1176_1205_ReachToShelf.mp4
38 | ReachToShelf/21_3_crop_2551_2583_ReachToShelf.mp4
39 | ReachToShelf/5_1_crop_2198_2212_ReachToShelf.mp4
40 | ReachToShelf/38_1_crop_1322_1359_ReachToShelf.mp4
41 | ReachToShelf/10_2_crop_1222_1255_ReachToShelf.mp4
42 | ReachToShelf/19_3_crop_3524_3556_ReachToShelf.mp4
43 | ReachToShelf/20_3_crop_349_387_ReachToShelf.mp4
44 | ReachToShelf/16_2_crop_3682_3731_ReachToShelf.mp4
45 | ReachToShelf/19_1_crop_180_238_ReachToShelf.mp4
46 | ReachToShelf/29_3_crop_751_776_ReachToShelf.mp4
47 | ReachToShelf/17_2_crop_3955_4004_ReachToShelf.mp4
48 | ReachToShelf/20_3_crop_2700_2733_ReachToShelf.mp4
49 | ReachToShelf/28_3_crop_750_782_ReachToShelf.mp4
50 | ReachToShelf/11_2_crop_699_749_ReachToShelf.mp4
51 | ReachToShelf/18_1_crop_1430_1465_ReachToShelf.mp4
52 | ReachToShelf/21_3_crop_558_584_ReachToShelf.mp4
53 | ReachToShelf/20_1_crop_1687_1731_ReachToShelf.mp4
54 | ReachToShelf/7_3_crop_3726_3762_ReachToShelf.mp4
55 | ReachToShelf/26_3_crop_1164_1184_ReachToShelf.mp4
56 | ReachToShelf/17_1_crop_391_414_ReachToShelf.mp4
57 | ReachToShelf/18_1_crop_2158_2194_ReachToShelf.mp4
58 | ReachToShelf/22_2_crop_3555_3583_ReachToShelf.mp4
59 | ReachToShelf/38_1_crop_3328_3359_ReachToShelf.mp4
60 | ReachToShelf/2_1_crop_292_327_ReachToShelf.mp4
61 | ReachToShelf/9_1_crop_538_558_ReachToShelf.mp4
62 | ReachToShelf/18_1_crop_770_804_ReachToShelf.mp4
63 | ReachToShelf/30_1_crop_3520_3538_ReachToShelf.mp4
64 | ReachToShelf/24_1_crop_3068_3086_ReachToShelf.mp4
65 | ReachToShelf/26_1_crop_2622_2687_ReachToShelf.mp4
66 | ReachToShelf/2_2_crop_1293_1322_ReachToShelf.mp4
67 | ReachToShelf/31_1_crop_2340_2389_ReachToShelf.mp4
68 | ReachToShelf/21_1_crop_2660_2678_ReachToShelf.mp4
69 | ReachToShelf/28_2_crop_1448_1472_ReachToShelf.mp4
70 | ReachToShelf/11_2_crop_581_613_ReachToShelf.mp4
71 | ReachToShelf/31_2_crop_3409_3429_ReachToShelf.mp4
72 | ReachToShelf/36_1_crop_2140_2170_ReachToShelf.mp4
73 | ReachToShelf/31_1_crop_2769_2797_ReachToShelf.mp4
74 | ReachToShelf/19_3_crop_3231_3258_ReachToShelf.mp4
75 | ReachToShelf/13_2_crop_2514_2530_ReachToShelf.mp4
76 | ReachToShelf/29_3_crop_5169_5208_ReachToShelf.mp4
77 | ReachToShelf/32_3_crop_624_646_ReachToShelf.mp4
78 | ReachToShelf/12_3_crop_2545_2586_ReachToShelf.mp4
79 | ReachToShelf/15_2_crop_2678_2704_ReachToShelf.mp4
80 | ReachToShelf/19_1_crop_571_594_ReachToShelf.mp4
81 | ReachToShelf/27_2_crop_1660_1690_ReachToShelf.mp4
82 | ReachToShelf/15_3_crop_836_872_ReachToShelf.mp4
83 | ReachToShelf/13_2_crop_2801_2852_ReachToShelf.mp4
84 | ReachToShelf/10_2_crop_1030_1049_ReachToShelf.mp4
85 | ReachToShelf/24_3_crop_1279_1313_ReachToShelf.mp4
86 | ReachToShelf/11_3_crop_2446_2478_ReachToShelf.mp4
87 | ReachToShelf/17_2_crop_2439_2455_ReachToShelf.mp4
88 | ReachToShelf/41_1_crop_3997_4068_ReachToShelf.mp4
89 | ReachToShelf/10_3_crop_2600_2638_ReachToShelf.mp4
90 | ReachToShelf/17_1_crop_1175_1217_ReachToShelf.mp4
91 | ReachToShelf/6_3_crop_2340_2378_ReachToShelf.mp4
92 | ReachToShelf/3_3_crop_1553_1579_ReachToShelf.mp4
93 | ReachToShelf/2_3_crop_2259_2282_ReachToShelf.mp4
94 | ReachToShelf/12_2_crop_293_331_ReachToShelf.mp4
95 | ReachToShelf/5_3_crop_1273_1307_ReachToShelf.mp4
96 | ReachToShelf/6_1_crop_456_492_ReachToShelf.mp4
97 | ReachToShelf/18_3_crop_1447_1484_ReachToShelf.mp4
98 | ReachToShelf/20_3_crop_976_994_ReachToShelf.mp4
99 | ReachToShelf/32_1_crop_3753_3806_ReachToShelf.mp4
100 | ReachToShelf/21_1_crop_1683_1700_ReachToShelf.mp4
101 | ReachToShelf/1_3_crop_3410_3440_ReachToShelf.mp4
102 | ReachToShelf/20_3_crop_552_609_ReachToShelf.mp4
103 | ReachToShelf/40_1_crop_3550_3589_ReachToShelf.mp4
104 | ReachToShelf/8_2_crop_3838_3876_ReachToShelf.mp4
105 | ReachToShelf/27_3_crop_3685_3733_ReachToShelf.mp4
106 | ReachToShelf/10_2_crop_762_797_ReachToShelf.mp4
107 | ReachToShelf/9_1_crop_913_935_ReachToShelf.mp4
108 | ReachToShelf/1_3_crop_829_853_ReachToShelf.mp4
109 | ReachToShelf/3_2_crop_986_1009_ReachToShelf.mp4
110 | ReachToShelf/40_1_crop_1510_1547_ReachToShelf.mp4
111 | ReachToShelf/24_3_crop_4292_4313_ReachToShelf.mp4
112 | ReachToShelf/14_2_crop_681_702_ReachToShelf.mp4
113 | ReachToShelf/19_1_crop_2920_2946_ReachToShelf.mp4
114 | ReachToShelf/19_3_crop_1218_1249_ReachToShelf.mp4
115 | ReachToShelf/17_2_crop_2215_2248_ReachToShelf.mp4
116 | ReachToShelf/28_3_crop_2657_2700_ReachToShelf.mp4
117 | ReachToShelf/4_2_crop_751_789_ReachToShelf.mp4
118 | ReachToShelf/2_3_crop_2525_2550_ReachToShelf.mp4
119 | ReachToShelf/10_2_crop_3142_3162_ReachToShelf.mp4
120 | ReachToShelf/12_1_crop_1377_1399_ReachToShelf.mp4
121 | ReachToShelf/33_1_crop_442_458_ReachToShelf.mp4
122 | ReachToShelf/30_3_crop_4033_4060_ReachToShelf.mp4
123 | ReachToShelf/27_3_crop_3459_3488_ReachToShelf.mp4
124 | ReachToShelf/21_2_crop_1687_1712_ReachToShelf.mp4
125 | ReachToShelf/6_1_crop_2441_2480_ReachToShelf.mp4
126 | ReachToShelf/41_2_crop_2532_2583_ReachToShelf.mp4
127 | ReachToShelf/7_1_crop_3128_3141_ReachToShelf.mp4
128 | ReachToShelf/23_2_crop_4630_4666_ReachToShelf.mp4
129 | ReachToShelf/32_1_crop_154_181_ReachToShelf.mp4
130 | ReachToShelf/30_3_crop_4138_4157_ReachToShelf.mp4
131 | ReachToShelf/10_3_crop_3616_3650_ReachToShelf.mp4
132 | ReachToShelf/18_1_crop_983_1036_ReachToShelf.mp4
133 | ReachToShelf/25_2_crop_3374_3402_ReachToShelf.mp4
134 | ReachToShelf/6_3_crop_815_850_ReachToShelf.mp4
135 | ReachToShelf/36_1_crop_1263_1296_ReachToShelf.mp4
136 | ReachToShelf/36_1_crop_897_934_ReachToShelf.mp4
137 | ReachToShelf/4_1_crop_294_327_ReachToShelf.mp4
138 | ReachToShelf/26_2_crop_1570_1611_ReachToShelf.mp4
139 | ReachToShelf/18_3_crop_2363_2396_ReachToShelf.mp4
140 | ReachToShelf/21_2_crop_2199_2224_ReachToShelf.mp4
141 | ReachToShelf/17_3_crop_1287_1324_ReachToShelf.mp4
142 | ReachToShelf/14_1_crop_3711_3744_ReachToShelf.mp4
143 | ReachToShelf/1_3_crop_1441_1462_ReachToShelf.mp4
144 | ReachToShelf/8_2_crop_1950_1979_ReachToShelf.mp4
145 | ReachToShelf/17_3_crop_4067_4101_ReachToShelf.mp4
146 | ReachToShelf/18_2_crop_524_545_ReachToShelf.mp4
147 | ReachToShelf/3_1_crop_1034_1061_ReachToShelf.mp4
148 | ReachToShelf/8_2_crop_2007_2034_ReachToShelf.mp4
149 | ReachToShelf/14_3_crop_394_419_ReachToShelf.mp4
150 | ReachToShelf/27_1_crop_2389_2419_ReachToShelf.mp4
151 | ReachToShelf/31_3_crop_1076_1105_ReachToShelf.mp4
152 | ReachToShelf/24_2_crop_3364_3393_ReachToShelf.mp4
153 | ReachToShelf/11_3_crop_3083_3107_ReachToShelf.mp4
154 | ReachToShelf/19_3_crop_1060_1086_ReachToShelf.mp4
155 | ReachToShelf/5_2_crop_1437_1471_ReachToShelf.mp4
156 | ReachToShelf/15_2_crop_1710_1740_ReachToShelf.mp4
157 | ReachToShelf/26_1_crop_4076_4102_ReachToShelf.mp4
158 | ReachToShelf/38_1_crop_3508_3563_ReachToShelf.mp4
159 | ReachToShelf/11_3_crop_2235_2252_ReachToShelf.mp4
160 | ReachToShelf/20_1_crop_923_941_ReachToShelf.mp4
161 | ReachToShelf/34_1_crop_3719_3766_ReachToShelf.mp4
162 | ReachToShelf/24_3_crop_4383_4404_ReachToShelf.mp4
163 | ReachToShelf/26_1_crop_2779_2812_ReachToShelf.mp4
164 | ReachToShelf/1_3_crop_2434_2469_ReachToShelf.mp4
165 | ReachToShelf/24_2_crop_3946_3981_ReachToShelf.mp4
166 | ReachToShelf/16_3_crop_468_483_ReachToShelf.mp4
167 | ReachToShelf/12_2_crop_1557_1582_ReachToShelf.mp4
168 | ReachToShelf/32_2_crop_3303_3327_ReachToShelf.mp4
169 | ReachToShelf/41_1_crop_2858_2880_ReachToShelf.mp4
170 | ReachToShelf/19_2_crop_3220_3234_ReachToShelf.mp4
171 | ReachToShelf/27_2_crop_2660_2678_ReachToShelf.mp4
172 | RetractFromShelf/15_3_crop_1007_1032_RetractFromShelf.mp4
173 | RetractFromShelf/22_3_crop_505_567_RetractFromShelf.mp4
174 | RetractFromShelf/2_2_crop_1323_1342_RetractFromShelf.mp4
175 | RetractFromShelf/25_1_crop_1262_1305_RetractFromShelf.mp4
176 | RetractFromShelf/2_1_crop_862_895_RetractFromShelf.mp4
177 | RetractFromShelf/13_2_crop_1367_1392_RetractFromShelf.mp4
178 | RetractFromShelf/20_3_crop_2115_2151_RetractFromShelf.mp4
179 | RetractFromShelf/15_2_crop_176_204_RetractFromShelf.mp4
180 | RetractFromShelf/18_2_crop_1033_1056_RetractFromShelf.mp4
181 | RetractFromShelf/18_2_crop_502_523_RetractFromShelf.mp4
182 | RetractFromShelf/18_2_crop_691_713_RetractFromShelf.mp4
183 | RetractFromShelf/37_1_crop_809_840_RetractFromShelf.mp4
184 | RetractFromShelf/18_1_crop_1652_1690_RetractFromShelf.mp4
185 | RetractFromShelf/28_3_crop_3356_3395_RetractFromShelf.mp4
186 | RetractFromShelf/39_1_crop_2377_2418_RetractFromShelf.mp4
187 | RetractFromShelf/6_3_crop_3731_3767_RetractFromShelf.mp4
188 | RetractFromShelf/31_1_crop_381_418_RetractFromShelf.mp4
189 | RetractFromShelf/13_3_crop_1777_1816_RetractFromShelf.mp4
190 | RetractFromShelf/25_2_crop_3843_3876_RetractFromShelf.mp4
191 | RetractFromShelf/22_2_crop_620_667_RetractFromShelf.mp4
192 | RetractFromShelf/30_3_crop_4061_4086_RetractFromShelf.mp4
193 | RetractFromShelf/18_2_crop_2504_2523_RetractFromShelf.mp4
194 | RetractFromShelf/2_2_crop_2907_2940_RetractFromShelf.mp4
195 | RetractFromShelf/26_1_crop_2514_2549_RetractFromShelf.mp4
196 | RetractFromShelf/11_1_crop_2506_2529_RetractFromShelf.mp4
197 | RetractFromShelf/7_1_crop_1741_1770_RetractFromShelf.mp4
198 | RetractFromShelf/14_3_crop_2606_2638_RetractFromShelf.mp4
199 | RetractFromShelf/36_1_crop_3029_3068_RetractFromShelf.mp4
200 | RetractFromShelf/12_1_crop_942_1004_RetractFromShelf.mp4
201 | RetractFromShelf/11_1_crop_2634_2671_RetractFromShelf.mp4
202 | RetractFromShelf/15_3_crop_1730_1787_RetractFromShelf.mp4
203 | RetractFromShelf/22_2_crop_2745_2807_RetractFromShelf.mp4
204 | RetractFromShelf/29_2_crop_1995_2026_RetractFromShelf.mp4
205 | RetractFromShelf/29_2_crop_3279_3313_RetractFromShelf.mp4
206 | RetractFromShelf/3_3_crop_879_932_RetractFromShelf.mp4
207 | RetractFromShelf/39_1_crop_343_420_RetractFromShelf.mp4
208 | RetractFromShelf/10_3_crop_2656_2694_RetractFromShelf.mp4
209 | RetractFromShelf/6_1_crop_587_639_RetractFromShelf.mp4
210 | RetractFromShelf/24_1_crop_2827_2859_RetractFromShelf.mp4
211 | RetractFromShelf/24_3_crop_4405_4474_RetractFromShelf.mp4
212 | RetractFromShelf/17_2_crop_3469_3485_RetractFromShelf.mp4
213 | RetractFromShelf/16_1_crop_2805_2822_RetractFromShelf.mp4
214 | RetractFromShelf/9_1_crop_2538_2601_RetractFromShelf.mp4
215 | RetractFromShelf/17_2_crop_1605_1637_RetractFromShelf.mp4
216 | RetractFromShelf/15_2_crop_2195_2213_RetractFromShelf.mp4
217 | RetractFromShelf/26_2_crop_3603_3633_RetractFromShelf.mp4
218 | RetractFromShelf/41_1_crop_1212_1250_RetractFromShelf.mp4
219 | RetractFromShelf/13_1_crop_3432_3469_RetractFromShelf.mp4
220 | RetractFromShelf/32_3_crop_2569_2616_RetractFromShelf.mp4
221 | RetractFromShelf/38_1_crop_1615_1664_RetractFromShelf.mp4
222 | RetractFromShelf/28_2_crop_3909_3953_RetractFromShelf.mp4
223 | RetractFromShelf/23_1_crop_1739_1798_RetractFromShelf.mp4
224 | RetractFromShelf/15_2_crop_996_1023_RetractFromShelf.mp4
225 | RetractFromShelf/28_1_crop_1691_1746_RetractFromShelf.mp4
226 | RetractFromShelf/6_3_crop_2379_2472_RetractFromShelf.mp4
227 | RetractFromShelf/32_3_crop_2241_2284_RetractFromShelf.mp4
228 | RetractFromShelf/19_1_crop_238_267_RetractFromShelf.mp4
229 | RetractFromShelf/21_1_crop_3148_3175_RetractFromShelf.mp4
230 | RetractFromShelf/32_3_crop_3332_3375_RetractFromShelf.mp4
231 | RetractFromShelf/19_2_crop_1381_1428_RetractFromShelf.mp4
232 | RetractFromShelf/5_2_crop_3147_3196_RetractFromShelf.mp4
233 | RetractFromShelf/15_2_crop_3307_3337_RetractFromShelf.mp4
234 | RetractFromShelf/16_1_crop_3904_3923_RetractFromShelf.mp4
235 | RetractFromShelf/28_3_crop_1386_1416_RetractFromShelf.mp4
236 | RetractFromShelf/7_2_crop_1900_1920_RetractFromShelf.mp4
237 | RetractFromShelf/40_1_crop_1811_1840_RetractFromShelf.mp4
238 | RetractFromShelf/22_3_crop_1049_1087_RetractFromShelf.mp4
239 | RetractFromShelf/7_2_crop_546_565_RetractFromShelf.mp4
240 | RetractFromShelf/17_3_crop_2392_2459_RetractFromShelf.mp4
241 | RetractFromShelf/7_2_crop_1094_1129_RetractFromShelf.mp4
242 | RetractFromShelf/26_1_crop_2816_2837_RetractFromShelf.mp4
243 | RetractFromShelf/14_2_crop_1102_1129_RetractFromShelf.mp4
244 | RetractFromShelf/7_1_crop_3448_3464_RetractFromShelf.mp4
245 | RetractFromShelf/17_2_crop_4011_4054_RetractFromShelf.mp4
246 | RetractFromShelf/28_2_crop_1474_1531_RetractFromShelf.mp4
247 | RetractFromShelf/12_1_crop_1402_1440_RetractFromShelf.mp4
248 | RetractFromShelf/41_1_crop_388_424_RetractFromShelf.mp4
249 | RetractFromShelf/4_1_crop_2770_2812_RetractFromShelf.mp4
250 | RetractFromShelf/26_1_crop_3010_3035_RetractFromShelf.mp4
251 | RetractFromShelf/26_1_crop_1691_1726_RetractFromShelf.mp4
252 | RetractFromShelf/12_2_crop_691_720_RetractFromShelf.mp4
253 | RetractFromShelf/21_1_crop_3976_3992_RetractFromShelf.mp4
254 | RetractFromShelf/29_3_crop_3646_3662_RetractFromShelf.mp4
255 | RetractFromShelf/8_2_crop_2072_2092_RetractFromShelf.mp4
256 | RetractFromShelf/1_1_crop_2936_2957_RetractFromShelf.mp4
257 | RetractFromShelf/20_1_crop_3142_3168_RetractFromShelf.mp4
258 | RetractFromShelf/26_1_crop_1812_1836_RetractFromShelf.mp4
259 | RetractFromShelf/7_3_crop_3072_3102_RetractFromShelf.mp4
260 | RetractFromShelf/4_3_crop_3027_3067_RetractFromShelf.mp4
261 | RetractFromShelf/14_1_crop_3425_3444_RetractFromShelf.mp4
262 | RetractFromShelf/1_2_crop_3406_3438_RetractFromShelf.mp4
263 | RetractFromShelf/12_1_crop_2812_2846_RetractFromShelf.mp4
264 | RetractFromShelf/19_1_crop_2972_3007_RetractFromShelf.mp4
265 | RetractFromShelf/28_3_crop_2467_2512_RetractFromShelf.mp4
266 | RetractFromShelf/6_2_crop_686_741_RetractFromShelf.mp4
267 | RetractFromShelf/14_3_crop_1542_1583_RetractFromShelf.mp4
268 | RetractFromShelf/6_2_crop_3502_3546_RetractFromShelf.mp4
269 | RetractFromShelf/6_2_crop_2475_2522_RetractFromShelf.mp4
270 | RetractFromShelf/18_2_crop_1094_1153_RetractFromShelf.mp4
271 | RetractFromShelf/5_3_crop_554_579_RetractFromShelf.mp4
272 | RetractFromShelf/14_3_crop_422_461_RetractFromShelf.mp4
273 | RetractFromShelf/11_3_crop_307_347_RetractFromShelf.mp4
274 | RetractFromShelf/6_2_crop_1713_1796_RetractFromShelf.mp4
275 | RetractFromShelf/38_1_crop_1396_1430_RetractFromShelf.mp4
276 | RetractFromShelf/18_2_crop_2225_2246_RetractFromShelf.mp4
277 | RetractFromShelf/7_1_crop_1280_1297_RetractFromShelf.mp4
278 | RetractFromShelf/6_3_crop_852_972_RetractFromShelf.mp4
279 | RetractFromShelf/27_2_crop_3267_3324_RetractFromShelf.mp4
280 | RetractFromShelf/13_1_crop_3684_3700_RetractFromShelf.mp4
281 | RetractFromShelf/32_2_crop_3327_3347_RetractFromShelf.mp4
282 | RetractFromShelf/5_3_crop_1309_1343_RetractFromShelf.mp4
283 | RetractFromShelf/4_3_crop_427_489_RetractFromShelf.mp4
284 | RetractFromShelf/1_1_crop_418_433_RetractFromShelf.mp4
285 | RetractFromShelf/27_2_crop_3052_3105_RetractFromShelf.mp4
286 | RetractFromShelf/33_1_crop_260_295_RetractFromShelf.mp4
287 | RetractFromShelf/27_1_crop_2608_2641_RetractFromShelf.mp4
288 | RetractFromShelf/22_3_crop_1805_1835_RetractFromShelf.mp4
289 | RetractFromShelf/25_3_crop_788_833_RetractFromShelf.mp4
290 | RetractFromShelf/21_1_crop_3877_3908_RetractFromShelf.mp4
291 | RetractFromShelf/4_2_crop_3709_3741_RetractFromShelf.mp4
292 | RetractFromShelf/17_2_crop_1482_1499_RetractFromShelf.mp4
293 | RetractFromShelf/32_1_crop_3203_3242_RetractFromShelf.mp4
294 | RetractFromShelf/24_1_crop_3533_3550_RetractFromShelf.mp4
295 | RetractFromShelf/32_1_crop_3676_3707_RetractFromShelf.mp4
296 | RetractFromShelf/4_1_crop_384_422_RetractFromShelf.mp4
297 | RetractFromShelf/2_1_crop_340_384_RetractFromShelf.mp4
298 | RetractFromShelf/20_2_crop_336_364_RetractFromShelf.mp4
299 | RetractFromShelf/4_1_crop_1718_1758_RetractFromShelf.mp4
300 | RetractFromShelf/18_1_crop_3558_3580_RetractFromShelf.mp4
301 | RetractFromShelf/7_3_crop_2755_2817_RetractFromShelf.mp4
302 | RetractFromShelf/13_1_crop_674_697_RetractFromShelf.mp4
303 | RetractFromShelf/15_2_crop_2299_2322_RetractFromShelf.mp4
304 | RetractFromShelf/26_1_crop_2326_2353_RetractFromShelf.mp4
305 | RetractFromShelf/18_3_crop_1518_1586_RetractFromShelf.mp4
306 | RetractFromShelf/19_1_crop_1065_1107_RetractFromShelf.mp4
307 | RetractFromShelf/32_3_crop_346_412_RetractFromShelf.mp4
308 | RetractFromShelf/30_2_crop_1479_1511_RetractFromShelf.mp4
309 | RetractFromShelf/20_3_crop_2875_2898_RetractFromShelf.mp4
310 | RetractFromShelf/39_1_crop_3092_3116_RetractFromShelf.mp4
311 | RetractFromShelf/2_1_crop_2180_2196_RetractFromShelf.mp4
312 | RetractFromShelf/23_1_crop_813_881_RetractFromShelf.mp4
313 | RetractFromShelf/39_1_crop_2788_2823_RetractFromShelf.mp4
314 | RetractFromShelf/4_2_crop_695_748_RetractFromShelf.mp4
315 | RetractFromShelf/3_3_crop_2337_2361_RetractFromShelf.mp4
316 | RetractFromShelf/15_3_crop_3308_3355_RetractFromShelf.mp4
317 | RetractFromShelf/6_1_crop_500_537_RetractFromShelf.mp4
318 | RetractFromShelf/16_3_crop_3457_3485_RetractFromShelf.mp4
319 | RetractFromShelf/20_2_crop_2933_2973_RetractFromShelf.mp4
320 | RetractFromShelf/15_3_crop_2868_2901_RetractFromShelf.mp4
321 | RetractFromShelf/32_2_crop_3278_3302_RetractFromShelf.mp4
322 | RetractFromShelf/1_3_crop_2471_2492_RetractFromShelf.mp4
323 | RetractFromShelf/32_1_crop_676_733_RetractFromShelf.mp4
324 | RetractFromShelf/10_3_crop_619_634_RetractFromShelf.mp4
325 | RetractFromShelf/18_1_crop_3703_3735_RetractFromShelf.mp4
326 | RetractFromShelf/39_1_crop_3779_3827_RetractFromShelf.mp4
327 | RetractFromShelf/1_2_crop_1909_1923_RetractFromShelf.mp4
328 | RetractFromShelf/7_2_crop_876_903_RetractFromShelf.mp4
329 | RetractFromShelf/17_1_crop_3094_3110_RetractFromShelf.mp4
330 | RetractFromShelf/10_1_crop_2396_2417_RetractFromShelf.mp4
331 | RetractFromShelf/2_1_crop_1522_1546_RetractFromShelf.mp4
332 | RetractFromShelf/2_1_crop_3351_3372_RetractFromShelf.mp4
333 | RetractFromShelf/20_1_crop_3374_3404_RetractFromShelf.mp4
334 | HandInShelf/34_1_crop_635_663_HandInShelf.mp4
335 | HandInShelf/19_2_crop_771_889_HandInShelf.mp4
336 | HandInShelf/8_1_crop_2788_3009_HandInShelf.mp4
337 | HandInShelf/38_1_crop_2459_2529_HandInShelf.mp4
338 | HandInShelf/30_3_crop_2747_2789_HandInShelf.mp4
339 | HandInShelf/16_2_crop_2768_2805_HandInShelf.mp4
340 | HandInShelf/32_2_crop_1645_1671_HandInShelf.mp4
341 | HandInShelf/24_3_crop_3761_3787_HandInShelf.mp4
342 | HandInShelf/39_1_crop_1482_1509_HandInShelf.mp4
343 | HandInShelf/26_2_crop_911_969_HandInShelf.mp4
344 | HandInShelf/2_2_crop_548_575_HandInShelf.mp4
345 | HandInShelf/11_2_crop_2493_2525_HandInShelf.mp4
346 | HandInShelf/19_1_crop_598_693_HandInShelf.mp4
347 | HandInShelf/25_2_crop_606_681_HandInShelf.mp4
348 | HandInShelf/33_1_crop_594_665_HandInShelf.mp4
349 | HandInShelf/26_3_crop_1289_1336_HandInShelf.mp4
350 | HandInShelf/19_3_crop_3020_3127_HandInShelf.mp4
351 | HandInShelf/25_3_crop_1045_1105_HandInShelf.mp4
352 | HandInShelf/2_1_crop_3035_3052_HandInShelf.mp4
353 | HandInShelf/40_1_crop_2683_2919_HandInShelf.mp4
354 | HandInShelf/20_3_crop_1409_1445_HandInShelf.mp4
355 | HandInShelf/28_3_crop_1087_1383_HandInShelf.mp4
356 | HandInShelf/16_2_crop_2629_2659_HandInShelf.mp4
357 | HandInShelf/15_2_crop_1741_1852_HandInShelf.mp4
358 | HandInShelf/1_3_crop_4197_4455_HandInShelf.mp4
359 | HandInShelf/31_2_crop_1118_1150_HandInShelf.mp4
360 | HandInShelf/34_1_crop_558_583_HandInShelf.mp4
361 | HandInShelf/21_2_crop_3181_3241_HandInShelf.mp4
362 | HandInShelf/16_1_crop_437_488_HandInShelf.mp4
363 | HandInShelf/21_2_crop_1712_1836_HandInShelf.mp4
364 | HandInShelf/32_2_crop_894_902_HandInShelf.mp4
365 | HandInShelf/21_2_crop_2225_2529_HandInShelf.mp4
366 | HandInShelf/30_2_crop_3391_3478_HandInShelf.mp4
367 | HandInShelf/8_3_crop_1401_1742_HandInShelf.mp4
368 | HandInShelf/4_3_crop_727_756_HandInShelf.mp4
369 | HandInShelf/12_1_crop_458_532_HandInShelf.mp4
370 | HandInShelf/29_3_crop_1101_1263_HandInShelf.mp4
371 | HandInShelf/1_3_crop_3755_3794_HandInShelf.mp4
372 | HandInShelf/2_2_crop_2452_2505_HandInShelf.mp4
373 | HandInShelf/10_2_crop_477_544_HandInShelf.mp4
374 | HandInShelf/31_3_crop_1493_1523_HandInShelf.mp4
375 | HandInShelf/24_1_crop_1699_1818_HandInShelf.mp4
376 | HandInShelf/16_1_crop_552_573_HandInShelf.mp4
377 | HandInShelf/19_3_crop_277_311_HandInShelf.mp4
378 | HandInShelf/2_1_crop_472_554_HandInShelf.mp4
379 | HandInShelf/16_1_crop_3891_3902_HandInShelf.mp4
380 | HandInShelf/17_1_crop_145_164_HandInShelf.mp4
381 | HandInShelf/19_1_crop_982_1065_HandInShelf.mp4
382 | HandInShelf/30_1_crop_2274_2334_HandInShelf.mp4
383 | HandInShelf/1_1_crop_406_416_HandInShelf.mp4
384 | HandInShelf/7_1_crop_819_918_HandInShelf.mp4
385 | HandInShelf/32_3_crop_1207_1374_HandInShelf.mp4
386 | HandInShelf/23_3_crop_4755_4824_HandInShelf.mp4
387 | HandInShelf/39_1_crop_3525_3587_HandInShelf.mp4
388 | HandInShelf/15_2_crop_728_749_HandInShelf.mp4
389 | HandInShelf/1_2_crop_2352_2374_HandInShelf.mp4
390 | InspectProduct/17_2_crop_1639_1853_InspectProduct.mp4
391 | InspectProduct/31_3_crop_1193_1302_InspectProduct.mp4
392 | InspectProduct/32_3_crop_2617_2883_InspectProduct.mp4
393 | InspectProduct/28_3_crop_237_459_InspectProduct.mp4
394 | InspectProduct/28_2_crop_3956_4142_InspectProduct.mp4
395 | InspectProduct/13_3_crop_3129_3345_InspectProduct.mp4
396 | InspectProduct/1_3_crop_1499_1594_InspectProduct.mp4
397 | InspectProduct/20_3_crop_4196_4248_InspectProduct.mp4
398 | InspectProduct/26_3_crop_605_763_InspectProduct.mp4
399 | InspectProduct/12_1_crop_3320_3515_InspectProduct.mp4
400 | InspectProduct/30_3_crop_2825_3459_InspectProduct.mp4
401 | InspectProduct/12_3_crop_2732_2760_InspectProduct.mp4
402 | InspectProduct/5_2_crop_3199_3357_InspectProduct.mp4
403 | InspectProduct/12_2_crop_1692_1763_InspectProduct.mp4
404 | InspectProduct/20_3_crop_429_551_InspectProduct.mp4
405 | InspectProduct/14_2_crop_1367_2016_InspectProduct.mp4
406 | InspectProduct/25_3_crop_834_851_InspectProduct.mp4
407 | InspectProduct/39_1_crop_2419_2457_InspectProduct.mp4
408 | InspectProduct/1_3_crop_1253_1382_InspectProduct.mp4
409 | InspectProduct/28_3_crop_2517_2534_InspectProduct.mp4
410 | InspectProduct/17_1_crop_2737_2903_InspectProduct.mp4
411 | InspectProduct/20_1_crop_2770_2797_InspectProduct.mp4
412 | InspectProduct/22_3_crop_569_683_InspectProduct.mp4
413 | InspectProduct/7_3_crop_2405_2431_InspectProduct.mp4
414 | InspectProduct/19_2_crop_2584_2619_InspectProduct.mp4
415 | InspectProduct/34_1_crop_3577_3717_InspectProduct.mp4
416 | InspectProduct/17_3_crop_1534_1585_InspectProduct.mp4
417 | InspectProduct/11_1_crop_809_1003_InspectProduct.mp4
418 | InspectProduct/28_1_crop_2957_3012_InspectProduct.mp4
419 | InspectProduct/12_2_crop_691_719_InspectProduct.mp4
420 | InspectProduct/32_2_crop_3614_3641_InspectProduct.mp4
421 | InspectProduct/31_3_crop_2268_2346_InspectProduct.mp4
422 | InspectProduct/21_3_crop_2629_2784_InspectProduct.mp4
423 | InspectProduct/4_2_crop_3554_3667_InspectProduct.mp4
424 | InspectProduct/10_2_crop_1357_1512_InspectProduct.mp4
425 | InspectProduct/10_3_crop_3110_3177_InspectProduct.mp4
426 | InspectProduct/25_1_crop_2365_2526_InspectProduct.mp4
427 | InspectProduct/20_1_crop_3201_3233_InspectProduct.mp4
428 | InspectProduct/17_1_crop_2091_2152_InspectProduct.mp4
429 | InspectProduct/29_2_crop_3015_3039_InspectProduct.mp4
430 | InspectProduct/3_3_crop_1629_1645_InspectProduct.mp4
431 | InspectProduct/37_1_crop_3741_3793_InspectProduct.mp4
432 | InspectProduct/9_3_crop_2729_2848_InspectProduct.mp4
433 | InspectProduct/3_1_crop_288_368_InspectProduct.mp4
434 | InspectProduct/7_1_crop_1184_1237_InspectProduct.mp4
435 | InspectProduct/16_1_crop_2365_2404_InspectProduct.mp4
436 | InspectProduct/15_2_crop_204_228_InspectProduct.mp4
437 | InspectProduct/5_2_crop_1114_1279_InspectProduct.mp4
438 | InspectProduct/34_1_crop_3306_3384_InspectProduct.mp4
439 | InspectProduct/15_3_crop_733_810_InspectProduct.mp4
440 | InspectProduct/22_1_crop_1124_1444_InspectProduct.mp4
441 | InspectProduct/26_2_crop_3553_3567_InspectProduct.mp4
442 | InspectProduct/13_1_crop_3473_3648_InspectProduct.mp4
443 | InspectProduct/17_2_crop_3315_3379_InspectProduct.mp4
444 | InspectProduct/7_2_crop_3439_3492_InspectProduct.mp4
445 | InspectProduct/35_1_crop_2831_2848_InspectProduct.mp4
446 | InspectProduct/14_1_crop_495_598_InspectProduct.mp4
447 | InspectProduct/30_2_crop_767_1432_InspectProduct.mp4
448 | InspectProduct/1_3_crop_2495_2640_InspectProduct.mp4
449 | InspectProduct/28_2_crop_3659_3858_InspectProduct.mp4
450 | InspectProduct/1_3_crop_2767_2784_InspectProduct.mp4
451 | InspectProduct/6_1_crop_3150_3211_InspectProduct.mp4
452 | InspectProduct/7_2_crop_3670_3704_InspectProduct.mp4
453 | InspectProduct/3_2_crop_1061_1096_InspectProduct.mp4
454 | InspectProduct/21_1_crop_2854_2919_InspectProduct.mp4
455 | InspectProduct/1_3_crop_667_829_InspectProduct.mp4
456 | InspectProduct/23_2_crop_2984_3032_InspectProduct.mp4
457 | InspectShelf/16_2_crop_3280_3426_InspectShelf.mp4
458 | InspectShelf/1_1_crop_3458_3575_InspectShelf.mp4
459 | InspectShelf/26_2_crop_1050_1161_InspectShelf.mp4
460 | InspectShelf/22_2_crop_1015_1173_InspectShelf.mp4
461 | InspectShelf/22_3_crop_1090_1252_InspectShelf.mp4
462 | InspectShelf/17_3_crop_3160_3264_InspectShelf.mp4
463 | InspectShelf/5_3_crop_582_615_InspectShelf.mp4
464 | InspectShelf/41_2_crop_1447_1523_InspectShelf.mp4
465 | InspectShelf/6_2_crop_3171_3461_InspectShelf.mp4
466 | InspectShelf/36_1_crop_821_896_InspectShelf.mp4
467 | InspectShelf/22_1_crop_3688_3827_InspectShelf.mp4
468 | InspectShelf/31_1_crop_3463_3518_InspectShelf.mp4
469 | InspectShelf/31_2_crop_1892_1955_InspectShelf.mp4
470 | InspectShelf/24_1_crop_890_1058_InspectShelf.mp4
471 | InspectShelf/32_1_crop_239_327_InspectShelf.mp4
472 | InspectShelf/39_1_crop_2997_3049_InspectShelf.mp4
473 | InspectShelf/5_1_crop_836_1037_InspectShelf.mp4
474 | InspectShelf/12_3_crop_749_836_InspectShelf.mp4
475 | InspectShelf/7_1_crop_1778_1889_InspectShelf.mp4
476 | InspectShelf/6_3_crop_1233_1274_InspectShelf.mp4
477 | InspectShelf/2_1_crop_1947_2057_InspectShelf.mp4
478 | InspectShelf/22_2_crop_460_585_InspectShelf.mp4
479 | InspectShelf/13_2_crop_2889_2932_InspectShelf.mp4
480 | InspectShelf/31_2_crop_1248_1470_InspectShelf.mp4
481 | InspectShelf/10_2_crop_579_615_InspectShelf.mp4
482 | InspectShelf/7_2_crop_1045_1055_InspectShelf.mp4
483 | InspectShelf/4_2_crop_849_1034_InspectShelf.mp4
484 | InspectShelf/39_1_crop_2460_2733_InspectShelf.mp4
485 | InspectShelf/17_3_crop_1189_1223_InspectShelf.mp4
486 | InspectShelf/12_3_crop_3296_3423_InspectShelf.mp4
487 | InspectShelf/11_3_crop_2815_3081_InspectShelf.mp4
488 | InspectShelf/12_3_crop_554_584_InspectShelf.mp4
489 | InspectShelf/2_2_crop_2161_2243_InspectShelf.mp4
490 | InspectShelf/22_3_crop_1955_1979_InspectShelf.mp4
491 | InspectShelf/5_2_crop_398_520_InspectShelf.mp4
492 | InspectShelf/29_1_crop_265_800_InspectShelf.mp4
493 | InspectShelf/36_1_crop_3187_3299_InspectShelf.mp4
494 | InspectShelf/26_1_crop_3329_3410_InspectShelf.mp4
495 | InspectShelf/2_2_crop_2755_2875_InspectShelf.mp4
496 | InspectShelf/38_1_crop_3602_3727_InspectShelf.mp4
497 | InspectShelf/32_2_crop_3675_3744_InspectShelf.mp4
498 | InspectShelf/12_2_crop_2591_2715_InspectShelf.mp4
499 | InspectShelf/19_2_crop_2622_2681_InspectShelf.mp4
500 | InspectShelf/4_2_crop_2869_3280_InspectShelf.mp4
501 | InspectShelf/20_1_crop_2654_2709_InspectShelf.mp4
502 | InspectShelf/28_2_crop_2463_2586_InspectShelf.mp4
503 | InspectShelf/17_3_crop_3129_3138_InspectShelf.mp4
504 | InspectShelf/13_3_crop_2801_3055_InspectShelf.mp4
505 | InspectShelf/12_2_crop_541_581_InspectShelf.mp4
506 | InspectShelf/24_3_crop_547_666_InspectShelf.mp4
507 | InspectShelf/4_3_crop_2902_2993_InspectShelf.mp4
508 | InspectShelf/27_2_crop_1072_1150_InspectShelf.mp4
509 | InspectShelf/33_1_crop_3635_3700_InspectShelf.mp4
510 | InspectShelf/27_2_crop_3413_3445_InspectShelf.mp4
511 | InspectShelf/10_1_crop_865_965_InspectShelf.mp4
512 | InspectShelf/2_3_crop_2219_2258_InspectShelf.mp4
513 | InspectShelf/8_3_crop_3630_3667_InspectShelf.mp4
514 | InspectShelf/15_1_crop_169_1208_InspectShelf.mp4
515 | InspectShelf/1_3_crop_580_611_InspectShelf.mp4
516 | InspectShelf/31_2_crop_2581_2639_InspectShelf.mp4
517 | InspectShelf/3_1_crop_1344_1585_InspectShelf.mp4
518 | InspectShelf/39_1_crop_862_1023_InspectShelf.mp4
519 | InspectShelf/4_2_crop_1143_1716_InspectShelf.mp4
520 | InspectShelf/25_2_crop_3877_4245_InspectShelf.mp4
521 | InspectShelf/22_2_crop_3063_3227_InspectShelf.mp4
522 | InspectShelf/4_3_crop_885_1015_InspectShelf.mp4
523 | InspectShelf/9_2_crop_1419_1557_InspectShelf.mp4
524 | InspectShelf/28_2_crop_3234_3507_InspectShelf.mp4
525 | InspectShelf/22_3_crop_3136_3226_InspectShelf.mp4
526 | InspectShelf/41_1_crop_565_651_InspectShelf.mp4
527 | InspectShelf/31_1_crop_1028_1233_InspectShelf.mp4
528 | InspectShelf/26_2_crop_3088_3256_InspectShelf.mp4
529 | InspectShelf/30_3_crop_2510_2578_InspectShelf.mp4
530 | InspectShelf/25_1_crop_232_264_InspectShelf.mp4
531 | InspectShelf/7_3_crop_2631_2716_InspectShelf.mp4
532 | InspectShelf/41_2_crop_2423_2532_InspectShelf.mp4
533 | InspectShelf/29_3_crop_4094_4343_InspectShelf.mp4
534 | InspectShelf/26_3_crop_1397_1885_InspectShelf.mp4
535 | InspectShelf/3_3_crop_2874_3078_InspectShelf.mp4
536 | InspectShelf/18_2_crop_3178_3366_InspectShelf.mp4
537 |
--------------------------------------------------------------------------------
/data/movefile.py:
--------------------------------------------------------------------------------
1 | """
2 | After extracting the RAR, we run this to move all the files into
3 | the appropriate train/test folders.
4 |
5 | Should only run this file once!
6 | """
7 | import os
8 | import os.path
9 |
10 | def get_train_test_lists(version='01'):
11 | """
12 | Using one of the train/test files (01, 02, or 03), get the filename
13 | breakdowns we'll later use to move everything.
14 | """
15 | # Get our files based on version.
16 | test_file = './ucfTrainTestlist/testlist' + version + '.txt'
17 | train_file = './ucfTrainTestlist/trainlist' + version + '.txt'
18 | val_file = './ucfTrainTestlist/validationlist' + version + '.txt'
19 | # Build the test list.
20 | with open(test_file) as fin:
21 | test_list = [row.strip() for row in list(fin)]
22 |
23 | # Build the train list. Extra step to remove the class index.
24 | with open(train_file) as fin:
25 | train_list = [row.strip() for row in list(fin)]
26 | train_list = [row.split(' ')[0] for row in train_list]
27 |
28 | with open(val_file) as fin:
29 | val_list = [row.strip() for row in list(fin)]
30 | val_list = [row.split(' ')[0] for row in val_list]
31 |
32 | # Set the groups in a dictionary.
33 | file_groups = {
34 | 'train': train_list,
35 | 'test': test_list,
36 | 'validation': val_list
37 | }
38 |
39 | return file_groups
40 |
41 | def move_files(file_groups):
42 | """This assumes all of our files are currently in _this_ directory.
43 | So move them to the appropriate spot. Only needs to happen once.
44 | """
45 | # Do each of our groups.
46 | for group, videos in file_groups.items():
47 |
48 | # Do each of our videos.
49 | for video in videos:
50 |
51 | # Get the parts.
52 | parts = video.split('/')
53 | classname = parts[0]
54 | filename = parts[1]
55 |
56 | # Check if this class exists.
57 | if not os.path.exists(group + '/' + classname):
58 | print("Creating folder for %s/%s" % (group, classname))
59 | os.makedirs(group + '/' + classname)
60 |
61 | # Check if we have already moved this file, or at least that it
62 | # exists to move.
63 | if not os.path.exists(filename):
64 | print("Can't find %s to move. Skipping." % (filename))
65 | continue
66 |
67 | # Move it.
68 | dest = group + '/' + classname + '/' + filename
69 | print("Moving %s to %s" % (filename, dest))
70 | os.rename(filename, dest)
71 |
72 | print("Done.")
73 |
74 | def main():
75 | """
76 | Go through each of our train/test text files and move the videos
77 | to the right place.
78 | """
79 | # Get the videos in groups so we can move them.
80 | group_lists = get_train_test_lists()
81 |
82 | # Move the files.
83 | move_files(group_lists)
84 |
85 | if __name__ == '__main__':
86 | main()
--------------------------------------------------------------------------------
/data/ucfTrainTestlist/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naviocean/pseudo-3d-pytorch/50297d11248630792709782f467982e80c281384/data/ucfTrainTestlist/.DS_Store
--------------------------------------------------------------------------------
/data/ucfTrainTestlist/classInd.txt:
--------------------------------------------------------------------------------
1 | 1 ApplyEyeMakeup
2 | 2 ApplyLipstick
3 | 3 Archery
4 | 4 BabyCrawling
5 | 5 BalanceBeam
6 | 6 BandMarching
7 | 7 BaseballPitch
8 | 8 Basketball
9 | 9 BasketballDunk
10 | 10 BenchPress
11 | 11 Biking
12 | 12 Billiards
13 | 13 BlowDryHair
14 | 14 BlowingCandles
15 | 15 BodyWeightSquats
16 | 16 Bowling
17 | 17 BoxingPunchingBag
18 | 18 BoxingSpeedBag
19 | 19 BreastStroke
20 | 20 BrushingTeeth
21 | 21 CleanAndJerk
22 | 22 CliffDiving
23 | 23 CricketBowling
24 | 24 CricketShot
25 | 25 CuttingInKitchen
26 | 26 Diving
27 | 27 Drumming
28 | 28 Fencing
29 | 29 FieldHockeyPenalty
30 | 30 FloorGymnastics
31 | 31 FrisbeeCatch
32 | 32 FrontCrawl
33 | 33 GolfSwing
34 | 34 Haircut
35 | 35 Hammering
36 | 36 HammerThrow
37 | 37 HandstandPushups
38 | 38 HandstandWalking
39 | 39 HeadMassage
40 | 40 HighJump
41 | 41 HorseRace
42 | 42 HorseRiding
43 | 43 HulaHoop
44 | 44 IceDancing
45 | 45 JavelinThrow
46 | 46 JugglingBalls
47 | 47 JumpingJack
48 | 48 JumpRope
49 | 49 Kayaking
50 | 50 Knitting
51 | 51 LongJump
52 | 52 Lunges
53 | 53 MilitaryParade
54 | 54 Mixing
55 | 55 MoppingFloor
56 | 56 Nunchucks
57 | 57 ParallelBars
58 | 58 PizzaTossing
59 | 59 PlayingCello
60 | 60 PlayingDaf
61 | 61 PlayingDhol
62 | 62 PlayingFlute
63 | 63 PlayingGuitar
64 | 64 PlayingPiano
65 | 65 PlayingSitar
66 | 66 PlayingTabla
67 | 67 PlayingViolin
68 | 68 PoleVault
69 | 69 PommelHorse
70 | 70 PullUps
71 | 71 Punch
72 | 72 PushUps
73 | 73 Rafting
74 | 74 RockClimbingIndoor
75 | 75 RopeClimbing
76 | 76 Rowing
77 | 77 SalsaSpin
78 | 78 ShavingBeard
79 | 79 Shotput
80 | 80 SkateBoarding
81 | 81 Skiing
82 | 82 Skijet
83 | 83 SkyDiving
84 | 84 SoccerJuggling
85 | 85 SoccerPenalty
86 | 86 StillRings
87 | 87 SumoWrestling
88 | 88 Surfing
89 | 89 Swing
90 | 90 TableTennisShot
91 | 91 TaiChi
92 | 92 TennisSwing
93 | 93 ThrowDiscus
94 | 94 TrampolineJumping
95 | 95 Typing
96 | 96 UnevenBars
97 | 97 VolleyballSpiking
98 | 98 WalkingWithDog
99 | 99 WallPushups
100 | 100 WritingOnBoard
101 | 101 YoYo
102 |
--------------------------------------------------------------------------------
/dog.0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naviocean/pseudo-3d-pytorch/50297d11248630792709782f467982e80c281384/dog.0.jpg
--------------------------------------------------------------------------------
/logger.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
4 |
5 |
6 | def Logger(name, log_file, level=logging.INFO):
7 | """Function setup as many loggers as you want"""
8 |
9 | handler = logging.FileHandler(log_file)
10 | handler.setFormatter(formatter)
11 |
12 | logger = logging.getLogger(name)
13 | logger.setLevel(level)
14 | logger.addHandler(handler)
15 |
16 | return logger
17 |
--------------------------------------------------------------------------------
/lr_scheduler.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from torch.optim import Optimizer
3 |
4 |
5 | class CyclicLR(object):
6 | """Sets the learning rate of each parameter group according to
7 | cyclical learning rate policy (CLR). The policy cycles the learning
8 | rate between two boundaries with a constant frequency, as detailed in
9 | the paper `Cyclical Learning Rates for Training Neural Networks`_.
10 | The distance between the two boundaries can be scaled on a per-iteration
11 | or per-cycle basis.
12 | Cyclical learning rate policy changes the learning rate after every batch.
13 | `batch_step` should be called after a batch has been used for training.
14 | To resume training, save `last_batch_iteration` and use it to instantiate `CycleLR`.
15 | This class has three built-in policies, as put forth in the paper:
16 | "triangular":
17 | A basic triangular cycle w/ no amplitude scaling.
18 | "triangular2":
19 | A basic triangular cycle that scales initial amplitude by half each cycle.
20 | "exp_range":
21 | A cycle that scales initial amplitude by gamma**(cycle iterations) at each
22 | cycle iteration.
23 | This implementation was adapted from the github repo: `bckenstler/CLR`_
24 | Args:
25 | optimizer (Optimizer): Wrapped optimizer.
26 | base_lr (float or list): Initial learning rate which is the
27 | lower boundary in the cycle for eachparam groups.
28 | Default: 0.001
29 | max_lr (float or list): Upper boundaries in the cycle for
30 | each parameter group. Functionally,
31 | it defines the cycle amplitude (max_lr - base_lr).
32 | The lr at any cycle is the sum of base_lr
33 | and some scaling of the amplitude; therefore
34 | max_lr may not actually be reached depending on
35 | scaling function. Default: 0.006
36 | step_size (int): Number of training iterations per
37 | half cycle. Authors suggest setting step_size
38 | 2-8 x training iterations in epoch. Default: 2000
39 | mode (str): One of {triangular, triangular2, exp_range}.
40 | Values correspond to policies detailed above.
41 | If scale_fn is not None, this argument is ignored.
42 | Default: 'triangular'
43 | gamma (float): Constant in 'exp_range' scaling function:
44 | gamma**(cycle iterations)
45 | Default: 1.0
46 | scale_fn (function): Custom scaling policy defined by a single
47 | argument lambda function, where
48 | 0 <= scale_fn(x) <= 1 for all x >= 0.
49 | mode paramater is ignored
50 | Default: None
51 | scale_mode (str): {'cycle', 'iterations'}.
52 | Defines whether scale_fn is evaluated on
53 | cycle number or cycle iterations (training
54 | iterations since start of cycle).
55 | Default: 'cycle'
56 | last_batch_iteration (int): The index of the last batch. Default: -1
57 | Example:
58 | >>> optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
59 | >>> scheduler = torch.optim.CyclicLR(optimizer)
60 | >>> data_loader = torch.utils.data.DataLoader(...)
61 | >>> for epoch in range(10):
62 | >>> for batch in data_loader:
63 | >>> scheduler.batch_step()
64 | >>> train_batch(...)
65 | .. _Cyclical Learning Rates for Training Neural Networks: https://arxiv.org/abs/1506.01186
66 | .. _bckenstler/CLR: https://github.com/bckenstler/CLR
67 | """
68 |
69 | def __init__(self, optimizer, base_lr=1e-3, max_lr=6e-3,
70 | step_size=2000, mode='triangular', gamma=1.,
71 | scale_fn=None, scale_mode='cycle', last_batch_iteration=-1):
72 |
73 | if not isinstance(optimizer, Optimizer):
74 | raise TypeError('{} is not an Optimizer'.format(
75 | type(optimizer).__name__))
76 | self.optimizer = optimizer
77 |
78 | if isinstance(base_lr, list) or isinstance(base_lr, tuple):
79 | if len(base_lr) != len(optimizer.param_groups):
80 | raise ValueError("expected {} base_lr, got {}".format(
81 | len(optimizer.param_groups), len(base_lr)))
82 | self.base_lrs = list(base_lr)
83 | else:
84 | self.base_lrs = [base_lr] * len(optimizer.param_groups)
85 |
86 | if isinstance(max_lr, list) or isinstance(max_lr, tuple):
87 | if len(max_lr) != len(optimizer.param_groups):
88 | raise ValueError("expected {} max_lr, got {}".format(
89 | len(optimizer.param_groups), len(max_lr)))
90 | self.max_lrs = list(max_lr)
91 | else:
92 | self.max_lrs = [max_lr] * len(optimizer.param_groups)
93 |
94 | self.step_size = step_size
95 |
96 | if mode not in ['triangular', 'triangular2', 'exp_range'] \
97 | and scale_fn is None:
98 | raise ValueError('mode is invalid and scale_fn is None')
99 |
100 | self.mode = mode
101 | self.gamma = gamma
102 |
103 | if scale_fn is None:
104 | if self.mode == 'triangular':
105 | self.scale_fn = self._triangular_scale_fn
106 | self.scale_mode = 'cycle'
107 | elif self.mode == 'triangular2':
108 | self.scale_fn = self._triangular2_scale_fn
109 | self.scale_mode = 'cycle'
110 | elif self.mode == 'exp_range':
111 | self.scale_fn = self._exp_range_scale_fn
112 | self.scale_mode = 'iterations'
113 | else:
114 | self.scale_fn = scale_fn
115 | self.scale_mode = scale_mode
116 |
117 | self.batch_step(last_batch_iteration + 1)
118 | self.last_batch_iteration = last_batch_iteration
119 |
120 | def batch_step(self, batch_iteration=None):
121 | if batch_iteration is None:
122 | batch_iteration = self.last_batch_iteration + 1
123 | self.last_batch_iteration = batch_iteration
124 | for param_group, lr in zip(self.optimizer.param_groups, self.get_lr()):
125 | param_group['lr'] = lr
126 |
127 | def _triangular_scale_fn(self, x):
128 | return 1.
129 |
130 | def _triangular2_scale_fn(self, x):
131 | return 1 / (2. ** (x - 1))
132 |
133 | def _exp_range_scale_fn(self, x):
134 | return self.gamma ** (x)
135 |
136 | def get_lr(self):
137 | step_size = float(self.step_size)
138 | cycle = np.floor(1 + self.last_batch_iteration / (2 * step_size))
139 | x = np.abs(self.last_batch_iteration / step_size - 2 * cycle + 1)
140 |
141 | lrs = []
142 | param_lrs = zip(self.optimizer.param_groups, self.base_lrs, self.max_lrs)
143 | for param_group, base_lr, max_lr in param_lrs:
144 | base_height = (max_lr - base_lr) * np.maximum(0, (1 - x))
145 | if self.scale_mode == 'cycle':
146 | lr = base_lr + base_height * self.scale_fn(cycle)
147 | else:
148 | lr = base_lr + base_height * self.scale_fn(self.last_batch_iteration)
149 | lrs.append(lr)
150 | return lrs
151 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import argparse
3 | from train import Training
4 | from test import Testing
5 |
6 | parser = argparse.ArgumentParser(description='PyTorch Pseudo-3D fine-tuning')
7 | parser.add_argument('data', metavar='DIR', help='path to dataset')
8 | parser.add_argument('--data-set', default='UCF101', const='UCF101', nargs='?', choices=['UCF101', 'Breakfast', 'merl'])
9 | parser.add_argument('--workers', default=4, type=int, metavar='N', help='number of data loading workers (default: 4)')
10 | parser.add_argument('--early-stop', default=10, type=int, metavar='N', help='number of early stopping')
11 | parser.add_argument('--epochs', default=75, type=int, metavar='N', help='number of total epochs to run')
12 | parser.add_argument('--start-epoch', default=0, type=int, metavar='N', help='manual epoch number (useful on restarts)')
13 | parser.add_argument('-b', '--batch-size', default=16, type=int, metavar='N', help='mini-batch size (default: 256)')
14 | parser.add_argument('--lr', '--learning-rate', default=1e-4, type=float, metavar='LR', help='initial learning rate')
15 | parser.add_argument('--momentum', default=0.9, type=float, metavar='M', help='momentum')
16 | parser.add_argument('--dropout', default=0.5, type=float, metavar='M', help='dropout')
17 | parser.add_argument('--weight-decay', default=1e-4, type=float, metavar='W', help='weight decay')
18 | parser.add_argument('--print-freq', default=1, type=int, metavar='N', help='print frequency')
19 | parser.add_argument('--resume', default='', type=str, metavar='PATH', help='path to latest checkpoint')
20 | parser.add_argument('--evaluate', dest='evaluate', action='store_true', help='evaluate model on validation set')
21 | parser.add_argument('--test', dest='test', action='store_true', help='evaluate model on test set')
22 | parser.add_argument('--random', dest='random', action='store_true', help='random pick image')
23 | parser.add_argument('--pretrained', dest='pretrained', action='store_true', help='use pre-trained model')
24 | parser.add_argument('--model-type', default='P3D', choices=['P3D', 'C3D', 'I3D'], help='which model to run the code')
25 | parser.add_argument('--num-frames', default=16, type=int, metavar='N', help='number frames per clip')
26 | parser.add_argument('--log-visualize', default='./runs', type=str, metavar='PATH', help='tensorboard log')
27 |
28 | def main():
29 | args = parser.parse_args()
30 | args = vars(args)
31 |
32 | if args['data_set'] == 'UCF101':
33 | print('UCF101 data set')
34 | name_list = 'ucfTrainTestlist'
35 | num_classes = 101
36 | elif args['data_set'] == 'Breakfast':
37 | print("breakfast data set")
38 | num_classes = 37
39 | name_list = 'breakfastTrainTestList'
40 | else:
41 | print('Merl data set')
42 | num_classes = 5
43 | name_list = 'merlTrainTestList'
44 |
45 | if args['test']:
46 | Testing(name_list=name_list, num_classes=num_classes, modality='RGB', **args)
47 | else:
48 | Training(name_list=name_list, num_classes=num_classes, modality='RGB', **args)
49 |
50 |
51 | if __name__ == '__main__':
52 | main()
53 |
--------------------------------------------------------------------------------
/meter.py:
--------------------------------------------------------------------------------
1 | class AverageMeter(object):
2 | """Computes and stores the average and current value"""
3 |
4 | def __init__(self):
5 | self.reset()
6 |
7 | def reset(self):
8 | self.val = 0
9 | self.avg = 0
10 | self.sum = 0
11 | self.count = 0
12 |
13 | def update(self, val, n=1):
14 | self.val = val
15 | self.sum += val * n
16 | self.count += n
17 | self.avg = self.sum / self.count
--------------------------------------------------------------------------------
/models/C3D.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 |
3 |
4 | class C3D(nn.Module):
5 | """
6 | The C3D network as described in [1].
7 | """
8 |
9 | def __init__(self):
10 | super(C3D, self).__init__()
11 |
12 | self.conv1 = nn.Conv3d(3, 64, kernel_size=(3, 3, 3), padding=(1, 1, 1))
13 | self.pool1 = nn.MaxPool3d(kernel_size=(1, 2, 2), stride=(1, 2, 2))
14 |
15 | self.conv2 = nn.Conv3d(64, 128, kernel_size=(3, 3, 3), padding=(1, 1, 1))
16 | self.pool2 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
17 |
18 | self.conv3a = nn.Conv3d(128, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1))
19 | self.conv3b = nn.Conv3d(256, 256, kernel_size=(3, 3, 3), padding=(1, 1, 1))
20 | self.pool3 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
21 |
22 | self.conv4a = nn.Conv3d(256, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
23 | self.conv4b = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
24 | self.pool4 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2))
25 |
26 | self.conv5a = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
27 | self.conv5b = nn.Conv3d(512, 512, kernel_size=(3, 3, 3), padding=(1, 1, 1))
28 | self.pool5 = nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2), padding=(0, 1, 1))
29 |
30 | self.fc6 = nn.Linear(8192, 4096)
31 | self.fc7 = nn.Linear(4096, 4096)
32 | self.fc8 = nn.Linear(4096, 487)
33 |
34 | self.dropout = nn.Dropout(p=0.5)
35 |
36 | self.relu = nn.ReLU()
37 | self.softmax = nn.Softmax()
38 |
39 | def forward(self, x):
40 |
41 | h = self.relu(self.conv1(x))
42 | h = self.pool1(h)
43 |
44 | h = self.relu(self.conv2(h))
45 | h = self.pool2(h)
46 |
47 | h = self.relu(self.conv3a(h))
48 | h = self.relu(self.conv3b(h))
49 | h = self.pool3(h)
50 |
51 | h = self.relu(self.conv4a(h))
52 | h = self.relu(self.conv4b(h))
53 | h = self.pool4(h)
54 |
55 | h = self.relu(self.conv5a(h))
56 | h = self.relu(self.conv5b(h))
57 | h = self.pool5(h)
58 |
59 | h = h.view(-1, 8192)
60 | h = self.relu(self.fc6(h))
61 | h = self.dropout(h)
62 | h = self.relu(self.fc7(h))
63 | h = self.dropout(h)
64 |
65 | logits = self.fc8(h)
66 | probs = self.softmax(logits)
67 |
68 | return probs
69 |
70 | """
71 | References
72 | ----------
73 | [1] Tran, Du, et al. "Learning spatiotemporal features with 3d convolutional networks."
74 | Proceedings of the IEEE international conference on computer vision. 2015.
75 | """
--------------------------------------------------------------------------------
/models/i3d.py:
--------------------------------------------------------------------------------
1 | import torch.nn as nn
2 | import torch
3 |
4 | class BasicConv3d(nn.Module):
5 | def __init__(self, in_planes, out_planes, kernel_size, stride, padding=0):
6 | super(BasicConv3d, self).__init__()
7 | self.conv = nn.Conv3d(in_planes, out_planes, kernel_size=kernel_size, stride=stride, padding=padding,
8 | bias=False) # verify bias false
9 |
10 | # verify defalt value in sonnet
11 | self.bn = nn.BatchNorm3d(out_planes, eps=1e-3, momentum=0.001, affine=True)
12 | self.relu = nn.ReLU(inplace=True)
13 |
14 | def forward(self, x):
15 | x = self.conv(x)
16 | x = self.bn(x)
17 | x = self.relu(x)
18 | return x
19 |
20 |
21 | class Mixed_3b(nn.Module):
22 | def __init__(self):
23 | super(Mixed_3b, self).__init__()
24 |
25 | self.branch0 = nn.Sequential(
26 | BasicConv3d(192, 64, kernel_size=1, stride=1),
27 | )
28 | self.branch1 = nn.Sequential(
29 | BasicConv3d(192, 96, kernel_size=1, stride=1),
30 | BasicConv3d(96, 128, kernel_size=3, stride=1, padding=1),
31 | )
32 | self.branch2 = nn.Sequential(
33 | BasicConv3d(192, 16, kernel_size=1, stride=1),
34 | BasicConv3d(16, 32, kernel_size=3, stride=1, padding=1),
35 | )
36 | self.branch3 = nn.Sequential(
37 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
38 | BasicConv3d(192, 32, kernel_size=1, stride=1),
39 | )
40 |
41 | def forward(self, x):
42 | x0 = self.branch0(x)
43 | x1 = self.branch1(x)
44 | x2 = self.branch2(x)
45 | x3 = self.branch3(x)
46 | out = torch.cat((x0, x1, x2, x3), 1)
47 |
48 | return out
49 |
50 |
51 | class Mixed_3c(nn.Module):
52 | def __init__(self):
53 | super(Mixed_3c, self).__init__()
54 | self.branch0 = nn.Sequential(
55 | BasicConv3d(256, 128, kernel_size=1, stride=1),
56 | )
57 | self.branch1 = nn.Sequential(
58 | BasicConv3d(256, 128, kernel_size=1, stride=1),
59 | BasicConv3d(128, 192, kernel_size=3, stride=1, padding=1),
60 | )
61 | self.branch2 = nn.Sequential(
62 | BasicConv3d(256, 32, kernel_size=1, stride=1),
63 | BasicConv3d(32, 96, kernel_size=3, stride=1, padding=1),
64 | )
65 | self.branch3 = nn.Sequential(
66 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
67 | BasicConv3d(256, 64, kernel_size=1, stride=1),
68 | )
69 |
70 | def forward(self, x):
71 | x0 = self.branch0(x)
72 | x1 = self.branch1(x)
73 | x2 = self.branch2(x)
74 | x3 = self.branch3(x)
75 | out = torch.cat((x0, x1, x2, x3), 1)
76 | return out
77 |
78 |
79 | class Mixed_4b(nn.Module):
80 | def __init__(self):
81 | super(Mixed_4b, self).__init__()
82 |
83 | self.branch0 = nn.Sequential(
84 | BasicConv3d(480, 192, kernel_size=1, stride=1),
85 | )
86 | self.branch1 = nn.Sequential(
87 | BasicConv3d(480, 96, kernel_size=1, stride=1),
88 | BasicConv3d(96, 208, kernel_size=3, stride=1, padding=1),
89 | )
90 | self.branch2 = nn.Sequential(
91 | BasicConv3d(480, 16, kernel_size=1, stride=1),
92 | BasicConv3d(16, 48, kernel_size=3, stride=1, padding=1),
93 | )
94 | self.branch3 = nn.Sequential(
95 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
96 | BasicConv3d(480, 64, kernel_size=1, stride=1),
97 | )
98 |
99 | def forward(self, x):
100 | x0 = self.branch0(x)
101 | x1 = self.branch1(x)
102 | x2 = self.branch2(x)
103 | x3 = self.branch3(x)
104 | out = torch.cat((x0, x1, x2, x3), 1)
105 | return out
106 |
107 |
108 | class Mixed_4c(nn.Module):
109 | def __init__(self):
110 | super(Mixed_4c, self).__init__()
111 |
112 | self.branch0 = nn.Sequential(
113 | BasicConv3d(512, 160, kernel_size=1, stride=1),
114 | )
115 | self.branch1 = nn.Sequential(
116 | BasicConv3d(512, 112, kernel_size=1, stride=1),
117 | BasicConv3d(112, 224, kernel_size=3, stride=1, padding=1),
118 | )
119 | self.branch2 = nn.Sequential(
120 | BasicConv3d(512, 24, kernel_size=1, stride=1),
121 | BasicConv3d(24, 64, kernel_size=3, stride=1, padding=1),
122 | )
123 | self.branch3 = nn.Sequential(
124 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
125 | BasicConv3d(512, 64, kernel_size=1, stride=1),
126 | )
127 |
128 | def forward(self, x):
129 | x0 = self.branch0(x)
130 | x1 = self.branch1(x)
131 | x2 = self.branch2(x)
132 | x3 = self.branch3(x)
133 | out = torch.cat((x0, x1, x2, x3), 1)
134 | return out
135 |
136 |
137 | class Mixed_4d(nn.Module):
138 | def __init__(self):
139 | super(Mixed_4d, self).__init__()
140 |
141 | self.branch0 = nn.Sequential(
142 | BasicConv3d(512, 128, kernel_size=1, stride=1),
143 | )
144 | self.branch1 = nn.Sequential(
145 | BasicConv3d(512, 128, kernel_size=1, stride=1),
146 | BasicConv3d(128, 256, kernel_size=3, stride=1, padding=1),
147 | )
148 | self.branch2 = nn.Sequential(
149 | BasicConv3d(512, 24, kernel_size=1, stride=1),
150 | BasicConv3d(24, 64, kernel_size=3, stride=1, padding=1),
151 | )
152 | self.branch3 = nn.Sequential(
153 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
154 | BasicConv3d(512, 64, kernel_size=1, stride=1),
155 | )
156 |
157 | def forward(self, x):
158 | x0 = self.branch0(x)
159 | x1 = self.branch1(x)
160 | x2 = self.branch2(x)
161 | x3 = self.branch3(x)
162 | out = torch.cat((x0, x1, x2, x3), 1)
163 | return out
164 |
165 |
166 | class Mixed_4e(nn.Module):
167 | def __init__(self):
168 | super(Mixed_4e, self).__init__()
169 |
170 | self.branch0 = nn.Sequential(
171 | BasicConv3d(512, 112, kernel_size=1, stride=1),
172 | )
173 | self.branch1 = nn.Sequential(
174 | BasicConv3d(512, 144, kernel_size=1, stride=1),
175 | BasicConv3d(144, 288, kernel_size=3, stride=1, padding=1),
176 | )
177 | self.branch2 = nn.Sequential(
178 | BasicConv3d(512, 32, kernel_size=1, stride=1),
179 | BasicConv3d(32, 64, kernel_size=3, stride=1, padding=1),
180 | )
181 | self.branch3 = nn.Sequential(
182 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
183 | BasicConv3d(512, 64, kernel_size=1, stride=1),
184 | )
185 |
186 | def forward(self, x):
187 | x0 = self.branch0(x)
188 | x1 = self.branch1(x)
189 | x2 = self.branch2(x)
190 | x3 = self.branch3(x)
191 | out = torch.cat((x0, x1, x2, x3), 1)
192 | return out
193 |
194 |
195 | class Mixed_4f(nn.Module):
196 | def __init__(self):
197 | super(Mixed_4f, self).__init__()
198 |
199 | self.branch0 = nn.Sequential(
200 | BasicConv3d(528, 256, kernel_size=1, stride=1),
201 | )
202 | self.branch1 = nn.Sequential(
203 | BasicConv3d(528, 160, kernel_size=1, stride=1),
204 | BasicConv3d(160, 320, kernel_size=3, stride=1, padding=1),
205 | )
206 | self.branch2 = nn.Sequential(
207 | BasicConv3d(528, 32, kernel_size=1, stride=1),
208 | BasicConv3d(32, 128, kernel_size=3, stride=1, padding=1),
209 | )
210 | self.branch3 = nn.Sequential(
211 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
212 | BasicConv3d(528, 128, kernel_size=1, stride=1),
213 | )
214 |
215 | def forward(self, x):
216 | x0 = self.branch0(x)
217 | x1 = self.branch1(x)
218 | x2 = self.branch2(x)
219 | x3 = self.branch3(x)
220 | out = torch.cat((x0, x1, x2, x3), 1)
221 | return out
222 |
223 |
224 | class Mixed_5b(nn.Module):
225 | def __init__(self):
226 | super(Mixed_5b, self).__init__()
227 |
228 | self.branch0 = nn.Sequential(
229 | BasicConv3d(832, 256, kernel_size=1, stride=1),
230 | )
231 | self.branch1 = nn.Sequential(
232 | BasicConv3d(832, 160, kernel_size=1, stride=1),
233 | BasicConv3d(160, 320, kernel_size=3, stride=1, padding=1),
234 | )
235 | self.branch2 = nn.Sequential(
236 | BasicConv3d(832, 32, kernel_size=1, stride=1),
237 | BasicConv3d(32, 128, kernel_size=3, stride=1, padding=1),
238 | )
239 | self.branch3 = nn.Sequential(
240 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
241 | BasicConv3d(832, 128, kernel_size=1, stride=1),
242 | )
243 |
244 | def forward(self, x):
245 | x0 = self.branch0(x)
246 | x1 = self.branch1(x)
247 | x2 = self.branch2(x)
248 | x3 = self.branch3(x)
249 | out = torch.cat((x0, x1, x2, x3), 1)
250 | return out
251 |
252 |
253 | class Mixed_5c(nn.Module):
254 | def __init__(self):
255 | super(Mixed_5c, self).__init__()
256 |
257 | self.branch0 = nn.Sequential(
258 | BasicConv3d(832, 384, kernel_size=1, stride=1),
259 | )
260 | self.branch1 = nn.Sequential(
261 | BasicConv3d(832, 192, kernel_size=1, stride=1),
262 | BasicConv3d(192, 384, kernel_size=3, stride=1, padding=1),
263 | )
264 | self.branch2 = nn.Sequential(
265 | BasicConv3d(832, 48, kernel_size=1, stride=1),
266 | BasicConv3d(48, 128, kernel_size=3, stride=1, padding=1),
267 | )
268 | self.branch3 = nn.Sequential(
269 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=1, padding=1),
270 | BasicConv3d(832, 128, kernel_size=1, stride=1),
271 | )
272 |
273 | def forward(self, x):
274 | x0 = self.branch0(x)
275 | x1 = self.branch1(x)
276 | x2 = self.branch2(x)
277 | x3 = self.branch3(x)
278 | out = torch.cat((x0, x1, x2, x3), 1)
279 | return out
280 |
281 | class I3D(nn.Module):
282 |
283 | def __init__(self, num_classes=400, dropout_keep_prob = 1, input_channel = 3, spatial_squeeze=True):
284 | super(I3D, self).__init__()
285 | self.features = nn.Sequential(
286 | BasicConv3d(input_channel, 64, kernel_size=7, stride=2, padding=3), # (64, 32, 112, 112)
287 | nn.MaxPool3d(kernel_size=(1,3,3), stride=(1,2,2), padding=(0,1,1)), # (64, 32, 56, 56)
288 | BasicConv3d(64, 64, kernel_size=1, stride=1), # (64, 32, 56, 56)
289 | BasicConv3d(64, 192, kernel_size=3, stride=1, padding=1), # (192, 32, 56, 56)
290 | nn.MaxPool3d(kernel_size=(1,3,3), stride=(1,2,2), padding=(0,1,1)), # (192, 32, 28, 28)
291 | Mixed_3b(), # (256, 32, 28, 28)
292 | Mixed_3c(), # (480, 32, 28, 28)
293 | nn.MaxPool3d(kernel_size=(3, 3, 3), stride=(2, 2, 2), padding=(1, 1, 1)), # (480, 16, 14, 14)
294 | Mixed_4b(),# (512, 16, 14, 14)
295 | Mixed_4c(),# (512, 16, 14, 14)
296 | Mixed_4d(),# (512, 16, 14, 14)
297 | Mixed_4e(),# (528, 16, 14, 14)
298 | Mixed_4f(),# (832, 16, 14, 14)
299 | nn.MaxPool3d(kernel_size=(2, 2, 2), stride=(2, 2, 2), padding=(0, 0, 0)), # (832, 8, 7, 7)
300 | Mixed_5b(), # (832, 8, 7, 7)
301 | Mixed_5c(), # (1024, 8, 7, 7)
302 | nn.AvgPool3d(kernel_size=(2, 7, 7), stride=1),# (1024, 8, 1, 1)
303 | nn.Dropout3d(dropout_keep_prob),
304 | nn.Conv3d(1024, num_classes, kernel_size=1, stride=1, bias=True),# (400, 8, 1, 1)
305 | )
306 | self.spatial_squeeze = spatial_squeeze
307 | self.softmax = nn.Softmax()
308 |
309 | def forward(self, x):
310 | logits = self.features(x)
311 |
312 | if self.spatial_squeeze:
313 | logits = logits.squeeze(3)
314 | logits = logits.squeeze(3)
315 |
316 | averaged_logits = torch.mean(logits, 2)
317 | predictions = self.softmax(averaged_logits)
318 |
319 | return predictions, averaged_logits
320 |
321 | if __name__ == '__main__':
322 |
323 | model = I3D()
324 | model.cuda()
325 | model = model.eval()
326 | data=torch.autograd.Variable(torch.rand(10,3,64,224,224)).cuda() # if modality=='Flow', please change the 2nd dimension 3==>2
327 | out=model(data)
328 | # print (model)
329 | print (out[0].size())
--------------------------------------------------------------------------------
/models/i3dpt.py:
--------------------------------------------------------------------------------
1 | import math
2 | import os
3 |
4 | import numpy as np
5 | import torch
6 | import torch.nn
7 | from torch.nn import ReplicationPad3d
8 | from torch.nn.modules.padding import ConstantPad3d
9 |
10 | def get_padding_shape(filter_shape, stride):
11 | def _pad_top_bottom(filter_dim, stride_val):
12 | pad_along = max(filter_dim - stride_val, 0)
13 | pad_top = pad_along // 2
14 | pad_bottom = pad_along - pad_top
15 | return pad_top, pad_bottom
16 |
17 | padding_shape = []
18 | for filter_dim, stride_val in zip(filter_shape, stride):
19 | pad_top, pad_bottom = _pad_top_bottom(filter_dim, stride_val)
20 | padding_shape.append(pad_top)
21 | padding_shape.append(pad_bottom)
22 | depth_top = padding_shape.pop(0)
23 | depth_bottom = padding_shape.pop(0)
24 | padding_shape.append(depth_top)
25 | padding_shape.append(depth_bottom)
26 |
27 | return tuple(padding_shape)
28 |
29 |
30 | def simplify_padding(padding_shapes):
31 | all_same = True
32 | padding_init = padding_shapes[0]
33 | for pad in padding_shapes[1:]:
34 | if pad != padding_init:
35 | all_same = False
36 | return all_same, padding_init
37 |
38 |
39 | class Unit3Dpy(torch.nn.Module):
40 | def __init__(self,
41 | in_channels,
42 | out_channels,
43 | kernel_size=(1, 1, 1),
44 | stride=(1, 1, 1),
45 | activation='relu',
46 | padding='SAME',
47 | use_bias=False,
48 | use_bn=True):
49 | super(Unit3Dpy, self).__init__()
50 |
51 | self.padding = padding
52 | self.activation = activation
53 | self.use_bn = use_bn
54 | if padding == 'SAME':
55 | padding_shape = get_padding_shape(kernel_size, stride)
56 | simplify_pad, pad_size = simplify_padding(padding_shape)
57 | self.simplify_pad = simplify_pad
58 | elif padding == 'VALID':
59 | padding_shape = 0
60 | else:
61 | raise ValueError(
62 | 'padding should be in [VALID|SAME] but got {}'.format(padding))
63 |
64 | if padding == 'SAME':
65 | if not simplify_pad:
66 | self.pad = torch.nn.ConstantPad3d(padding_shape, 0)
67 | self.conv3d = torch.nn.Conv3d(
68 | in_channels,
69 | out_channels,
70 | kernel_size,
71 | stride=stride,
72 | bias=use_bias)
73 | else:
74 | self.conv3d = torch.nn.Conv3d(
75 | in_channels,
76 | out_channels,
77 | kernel_size,
78 | stride=stride,
79 | padding=pad_size,
80 | bias=use_bias)
81 | elif padding == 'VALID':
82 | self.conv3d = torch.nn.Conv3d(
83 | in_channels,
84 | out_channels,
85 | kernel_size,
86 | padding=padding_shape,
87 | stride=stride,
88 | bias=use_bias)
89 | else:
90 | raise ValueError(
91 | 'padding should be in [VALID|SAME] but got {}'.format(padding))
92 |
93 | if self.use_bn:
94 | self.batch3d = torch.nn.BatchNorm3d(out_channels)
95 |
96 | if activation == 'relu':
97 | self.activation = torch.nn.functional.relu
98 |
99 | def forward(self, inp):
100 | if self.padding == 'SAME' and self.simplify_pad is False:
101 | inp = self.pad(inp)
102 | out = self.conv3d(inp)
103 | if self.use_bn:
104 | out = self.batch3d(out)
105 | if self.activation is not None:
106 | out = torch.nn.functional.relu(out)
107 | return out
108 |
109 |
110 | class MaxPool3dTFPadding(torch.nn.Module):
111 | def __init__(self, kernel_size, stride=None, padding='SAME'):
112 | super(MaxPool3dTFPadding, self).__init__()
113 | if padding == 'SAME':
114 | padding_shape = get_padding_shape(kernel_size, stride)
115 | self.padding_shape = padding_shape
116 | self.pad = torch.nn.ConstantPad3d(padding_shape, 0)
117 | self.pool = torch.nn.MaxPool3d(kernel_size, stride)
118 |
119 | def forward(self, inp):
120 | inp = self.pad(inp)
121 | out = self.pool(inp)
122 | return out
123 |
124 |
125 | class Mixed(torch.nn.Module):
126 | def __init__(self, in_channels, out_channels):
127 | super(Mixed, self).__init__()
128 | # Branch 0
129 | self.branch_0 = Unit3Dpy(
130 | in_channels, out_channels[0], kernel_size=(1, 1, 1))
131 |
132 | # Branch 1
133 | branch_1_conv1 = Unit3Dpy(
134 | in_channels, out_channels[1], kernel_size=(1, 1, 1))
135 | branch_1_conv2 = Unit3Dpy(
136 | out_channels[1], out_channels[2], kernel_size=(3, 3, 3))
137 | self.branch_1 = torch.nn.Sequential(branch_1_conv1, branch_1_conv2)
138 |
139 | # Branch 2
140 | branch_2_conv1 = Unit3Dpy(
141 | in_channels, out_channels[3], kernel_size=(1, 1, 1))
142 | branch_2_conv2 = Unit3Dpy(
143 | out_channels[3], out_channels[4], kernel_size=(3, 3, 3))
144 | self.branch_2 = torch.nn.Sequential(branch_2_conv1, branch_2_conv2)
145 |
146 | # Branch3
147 | branch_3_pool = MaxPool3dTFPadding(
148 | kernel_size=(3, 3, 3), stride=(1, 1, 1), padding='SAME')
149 | branch_3_conv2 = Unit3Dpy(
150 | in_channels, out_channels[5], kernel_size=(1, 1, 1))
151 | self.branch_3 = torch.nn.Sequential(branch_3_pool, branch_3_conv2)
152 |
153 | def forward(self, inp):
154 | out_0 = self.branch_0(inp)
155 | out_1 = self.branch_1(inp)
156 | out_2 = self.branch_2(inp)
157 | out_3 = self.branch_3(inp)
158 | out = torch.cat((out_0, out_1, out_2, out_3), 1)
159 | return out
160 |
161 |
162 | class I3D(torch.nn.Module):
163 | def __init__(self,
164 | num_classes,
165 | modality='rgb',
166 | dropout_keep_prob=1,
167 | name='inception'):
168 | super(I3D, self).__init__()
169 |
170 | self.name = name
171 | self.num_classes = num_classes
172 | if modality == 'rgb':
173 | in_channels = 3
174 | elif modality == 'flow':
175 | in_channels = 2
176 | else:
177 | raise ValueError(
178 | '{} not among known modalities [rgb|flow]'.format(modality))
179 | self.modality = modality
180 |
181 | conv3d_1a_7x7 = Unit3Dpy(
182 | out_channels=64,
183 | in_channels=in_channels,
184 | kernel_size=(7, 7, 7),
185 | stride=(2, 2, 2),
186 | padding='SAME')
187 | # 1st conv-pool
188 | self.conv3d_1a_7x7 = conv3d_1a_7x7
189 | self.maxPool3d_2a_3x3 = MaxPool3dTFPadding(
190 | kernel_size=(1, 3, 3), stride=(1, 2, 2), padding='SAME')
191 | # conv conv
192 | conv3d_2b_1x1 = Unit3Dpy(
193 | out_channels=64,
194 | in_channels=64,
195 | kernel_size=(1, 1, 1),
196 | padding='SAME')
197 | self.conv3d_2b_1x1 = conv3d_2b_1x1
198 | conv3d_2c_3x3 = Unit3Dpy(
199 | out_channels=192,
200 | in_channels=64,
201 | kernel_size=(3, 3, 3),
202 | padding='SAME')
203 | self.conv3d_2c_3x3 = conv3d_2c_3x3
204 | self.maxPool3d_3a_3x3 = MaxPool3dTFPadding(
205 | kernel_size=(1, 3, 3), stride=(1, 2, 2), padding='SAME')
206 |
207 | # Mixed_3b
208 | self.mixed_3b = Mixed(192, [64, 96, 128, 16, 32, 32])
209 | self.mixed_3c = Mixed(256, [128, 128, 192, 32, 96, 64])
210 |
211 | self.maxPool3d_4a_3x3 = MaxPool3dTFPadding(
212 | kernel_size=(3, 3, 3), stride=(2, 2, 2), padding='SAME')
213 |
214 | # Mixed 4
215 | self.mixed_4b = Mixed(480, [192, 96, 208, 16, 48, 64])
216 | self.mixed_4c = Mixed(512, [160, 112, 224, 24, 64, 64])
217 | self.mixed_4d = Mixed(512, [128, 128, 256, 24, 64, 64])
218 | self.mixed_4e = Mixed(512, [112, 144, 288, 32, 64, 64])
219 | self.mixed_4f = Mixed(528, [256, 160, 320, 32, 128, 128])
220 |
221 | # Ugly hack because I didn't use tensorflow's exact padding function
222 | self.pad_5a = torch.nn.ConstantPad3d((0, 0, 0, 0, 0, 1), 0)
223 | self.maxPool3d_5a_2x2 = MaxPool3dTFPadding(
224 | kernel_size=(2, 2, 2), stride=(2, 2, 2), padding='SAME')
225 |
226 | # Mixed 5
227 | self.mixed_5b = Mixed(832, [256, 160, 320, 32, 128, 128])
228 | self.mixed_5c = Mixed(832, [384, 192, 384, 48, 128, 128])
229 |
230 | self.avg_pool = torch.nn.AvgPool3d((2, 7, 7), (1, 1, 1))
231 | self.dropout = torch.nn.Dropout(dropout_keep_prob)
232 | self.conv3d_0c_1x1 = Unit3Dpy(
233 | in_channels=1024,
234 | out_channels=self.num_classes,
235 | kernel_size=(1, 1, 1),
236 | activation=None,
237 | use_bias=True,
238 | use_bn=False)
239 | self.softmax = torch.nn.Softmax(1)
240 |
241 | def forward(self, inp):
242 | # Preprocessing
243 | out = self.conv3d_1a_7x7(inp)
244 | out = self.maxPool3d_2a_3x3(out)
245 | out = self.conv3d_2b_1x1(out)
246 | out = self.conv3d_2c_3x3(out)
247 | out = self.maxPool3d_3a_3x3(out)
248 | out = self.mixed_3b(out)
249 | out = self.mixed_3c(out)
250 | out = self.maxPool3d_4a_3x3(out)
251 | out = self.mixed_4b(out)
252 | out = self.mixed_4c(out)
253 | out = self.mixed_4d(out)
254 | out = self.mixed_4e(out)
255 | out = self.mixed_4f(out)
256 | out = self.pad_5a(out)
257 | out = self.maxPool3d_5a_2x2(out)
258 | out = self.mixed_5b(out)
259 | out = self.mixed_5c(out)
260 | out = self.avg_pool(out)
261 | out = self.dropout(out)
262 | out = self.conv3d_0c_1x1(out)
263 | out = out.squeeze(3)
264 | out = out.squeeze(3)
265 | out = out.mean(2)
266 | out_logits = out
267 | out = self.softmax(out_logits)
268 | return out, out_logits
269 |
270 | def load_tf_weights(self, sess):
271 | state_dict = {}
272 | if self.modality == 'rgb':
273 | prefix = 'RGB/inception_i3d'
274 | elif self.modality == 'flow':
275 | prefix = 'Flow/inception_i3d'
276 | load_conv3d(state_dict, 'conv3d_1a_7x7', sess,
277 | os.path.join(prefix, 'Conv3d_1a_7x7'))
278 | load_conv3d(state_dict, 'conv3d_2b_1x1', sess,
279 | os.path.join(prefix, 'Conv3d_2b_1x1'))
280 | load_conv3d(state_dict, 'conv3d_2c_3x3', sess,
281 | os.path.join(prefix, 'Conv3d_2c_3x3'))
282 |
283 | load_mixed(state_dict, 'mixed_3b', sess,
284 | os.path.join(prefix, 'Mixed_3b'))
285 | load_mixed(state_dict, 'mixed_3c', sess,
286 | os.path.join(prefix, 'Mixed_3c'))
287 | load_mixed(state_dict, 'mixed_4b', sess,
288 | os.path.join(prefix, 'Mixed_4b'))
289 | load_mixed(state_dict, 'mixed_4c', sess,
290 | os.path.join(prefix, 'Mixed_4c'))
291 | load_mixed(state_dict, 'mixed_4d', sess,
292 | os.path.join(prefix, 'Mixed_4d'))
293 | load_mixed(state_dict, 'mixed_4e', sess,
294 | os.path.join(prefix, 'Mixed_4e'))
295 | # Here goest to 0.1 max error with tf
296 | load_mixed(state_dict, 'mixed_4f', sess,
297 | os.path.join(prefix, 'Mixed_4f'))
298 |
299 | load_mixed(
300 | state_dict,
301 | 'mixed_5b',
302 | sess,
303 | os.path.join(prefix, 'Mixed_5b'),
304 | fix_typo=True)
305 | load_mixed(state_dict, 'mixed_5c', sess,
306 | os.path.join(prefix, 'Mixed_5c'))
307 | load_conv3d(
308 | state_dict,
309 | 'conv3d_0c_1x1',
310 | sess,
311 | os.path.join(prefix, 'Logits', 'Conv3d_0c_1x1'),
312 | bias=True,
313 | bn=False)
314 | self.load_state_dict(state_dict)
315 |
316 |
317 | def get_conv_params(sess, name, bias=False):
318 | # Get conv weights
319 | conv_weights_tensor = sess.graph.get_tensor_by_name(
320 | os.path.join(name, 'w:0'))
321 | if bias:
322 | conv_bias_tensor = sess.graph.get_tensor_by_name(
323 | os.path.join(name, 'b:0'))
324 | conv_bias = sess.run(conv_bias_tensor)
325 | conv_weights = sess.run(conv_weights_tensor)
326 | conv_shape = conv_weights.shape
327 |
328 | kernel_shape = conv_shape[0:3]
329 | in_channels = conv_shape[3]
330 | out_channels = conv_shape[4]
331 |
332 | conv_op = sess.graph.get_operation_by_name(
333 | os.path.join(name, 'convolution'))
334 | padding_name = conv_op.get_attr('padding')
335 | padding = _get_padding(padding_name, kernel_shape)
336 | all_strides = conv_op.get_attr('strides')
337 | strides = all_strides[1:4]
338 | conv_params = [
339 | conv_weights, kernel_shape, in_channels, out_channels, strides, padding
340 | ]
341 | if bias:
342 | conv_params.append(conv_bias)
343 | return conv_params
344 |
345 |
346 | def get_bn_params(sess, name):
347 | moving_mean_tensor = sess.graph.get_tensor_by_name(
348 | os.path.join(name, 'moving_mean:0'))
349 | moving_var_tensor = sess.graph.get_tensor_by_name(
350 | os.path.join(name, 'moving_variance:0'))
351 | beta_tensor = sess.graph.get_tensor_by_name(os.path.join(name, 'beta:0'))
352 | moving_mean = sess.run(moving_mean_tensor)
353 | moving_var = sess.run(moving_var_tensor)
354 | beta = sess.run(beta_tensor)
355 | return moving_mean, moving_var, beta
356 |
357 |
358 | def _get_padding(padding_name, conv_shape):
359 | padding_name = padding_name.decode("utf-8")
360 | if padding_name == "VALID":
361 | return [0, 0]
362 | elif padding_name == "SAME":
363 | #return [math.ceil(int(conv_shape[0])/2), math.ceil(int(conv_shape[1])/2)]
364 | return [
365 | math.floor(int(conv_shape[0]) / 2),
366 | math.floor(int(conv_shape[1]) / 2),
367 | math.floor(int(conv_shape[2]) / 2)
368 | ]
369 | else:
370 | raise ValueError('Invalid padding name ' + padding_name)
371 |
372 |
373 | def load_conv3d(state_dict, name_pt, sess, name_tf, bias=False, bn=True):
374 | # Transfer convolution params
375 | conv_name_tf = os.path.join(name_tf, 'conv_3d')
376 | conv_params = get_conv_params(sess, conv_name_tf, bias=bias)
377 | if bias:
378 | conv_weights, kernel_shape, in_channels, out_channels, strides, padding, conv_bias = conv_params
379 | else:
380 | conv_weights, kernel_shape, in_channels, out_channels, strides, padding = conv_params
381 |
382 | conv_weights_rs = np.transpose(
383 | conv_weights, (4, 3, 0, 1,
384 | 2)) # to pt format (out_c, in_c, depth, height, width)
385 | state_dict[name_pt + '.conv3d.weight'] = torch.from_numpy(conv_weights_rs)
386 | if bias:
387 | state_dict[name_pt + '.conv3d.bias'] = torch.from_numpy(conv_bias)
388 |
389 | # Transfer batch norm params
390 | if bn:
391 | conv_tf_name = os.path.join(name_tf, 'batch_norm')
392 | moving_mean, moving_var, beta = get_bn_params(sess, conv_tf_name)
393 |
394 | out_planes = conv_weights_rs.shape[0]
395 | state_dict[name_pt + '.batch3d.weight'] = torch.ones(out_planes)
396 | state_dict[name_pt + '.batch3d.bias'] = torch.from_numpy(beta)
397 | state_dict[name_pt
398 | + '.batch3d.running_mean'] = torch.from_numpy(moving_mean)
399 | state_dict[name_pt
400 | + '.batch3d.running_var'] = torch.from_numpy(moving_var)
401 |
402 |
403 | def load_mixed(state_dict, name_pt, sess, name_tf, fix_typo=False):
404 | # Branch 0
405 | load_conv3d(state_dict, name_pt + '.branch_0', sess,
406 | os.path.join(name_tf, 'Branch_0/Conv3d_0a_1x1'))
407 |
408 | # Branch .1
409 | load_conv3d(state_dict, name_pt + '.branch_1.0', sess,
410 | os.path.join(name_tf, 'Branch_1/Conv3d_0a_1x1'))
411 | load_conv3d(state_dict, name_pt + '.branch_1.1', sess,
412 | os.path.join(name_tf, 'Branch_1/Conv3d_0b_3x3'))
413 |
414 | # Branch 2
415 | load_conv3d(state_dict, name_pt + '.branch_2.0', sess,
416 | os.path.join(name_tf, 'Branch_2/Conv3d_0a_1x1'))
417 | if fix_typo:
418 | load_conv3d(state_dict, name_pt + '.branch_2.1', sess,
419 | os.path.join(name_tf, 'Branch_2/Conv3d_0a_3x3'))
420 | else:
421 | load_conv3d(state_dict, name_pt + '.branch_2.1', sess,
422 | os.path.join(name_tf, 'Branch_2/Conv3d_0b_3x3'))
423 |
424 | # Branch 3
425 | load_conv3d(state_dict, name_pt + '.branch_3.1', sess,
426 | os.path.join(name_tf, 'Branch_3/Conv3d_0b_1x1'))
427 |
428 | if __name__ == '__main__':
429 |
430 | model = I3D(num_classes=400, modality='rgb')
431 | data=torch.autograd.Variable(torch.rand(10,3,16,224,224)) # if modality=='Flow', please change the 2nd dimension 3==>2
432 | out=model(data)
433 | print (model)
434 | print (out[0].size())
--------------------------------------------------------------------------------
/models/p3d_model.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import torch
3 | import torch.nn as nn
4 | import numpy as np
5 | import torch.nn.functional as F
6 | from torch.autograd import Variable
7 | import math
8 | from functools import partial
9 | import logging
10 |
11 | __all__ = ['P3D', 'P3D63', 'P3D131', 'P3D199']
12 |
13 |
14 | def conv_S(in_planes, out_planes, stride=1, padding=1):
15 | # as is descriped, conv S is 1x3x3
16 | return nn.Conv3d(in_planes, out_planes, kernel_size=(1, 3, 3), stride=1,
17 | padding=padding, bias=False)
18 |
19 |
20 | def conv_T(in_planes, out_planes, stride=1, padding=1):
21 | # conv T is 3x1x1
22 | return nn.Conv3d(in_planes, out_planes, kernel_size=(3, 1, 1), stride=1,
23 | padding=padding, bias=False)
24 |
25 |
26 | def downsample_basic_block(x, planes, stride):
27 | out = F.avg_pool3d(x, kernel_size=1, stride=stride)
28 | zero_pads = torch.Tensor(out.size(0), planes - out.size(1),
29 | out.size(2), out.size(3),
30 | out.size(4)).zero_()
31 | if isinstance(out.data, torch.cuda.FloatTensor):
32 | zero_pads = zero_pads.cuda()
33 |
34 | out = Variable(torch.cat([out.data, zero_pads], dim=1))
35 |
36 | return out
37 |
38 |
39 | class Bottleneck(nn.Module):
40 | expansion = 4
41 |
42 | def __init__(self, inplanes, planes, stride=1, downsample=None, n_s=0, depth_3d=47, ST_struc=('A', 'B', 'C')):
43 | super(Bottleneck, self).__init__()
44 | self.downsample = downsample
45 | self.depth_3d = depth_3d
46 | self.ST_struc = ST_struc
47 | self.len_ST = len(self.ST_struc)
48 |
49 | stride_p = stride
50 | if not self.downsample == None:
51 | stride_p = (1, 2, 2)
52 | if n_s < self.depth_3d:
53 | if n_s == 0:
54 | stride_p = 1
55 | self.conv1 = nn.Conv3d(inplanes, planes, kernel_size=1, bias=False, stride=stride_p)
56 | self.bn1 = nn.BatchNorm3d(planes)
57 | else:
58 | if n_s == self.depth_3d:
59 | stride_p = 2
60 | else:
61 | stride_p = 1
62 | self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False, stride=stride_p)
63 | self.bn1 = nn.BatchNorm2d(planes)
64 | # self.conv2 = nn.Conv3d(planes, planes, kernel_size=3, stride=stride,
65 | # padding=1, bias=False)
66 | self.id = n_s
67 | self.ST = list(self.ST_struc)[self.id % self.len_ST]
68 | if self.id < self.depth_3d:
69 | self.conv2 = conv_S(planes, planes, stride=1, padding=(0, 1, 1))
70 | self.bn2 = nn.BatchNorm3d(planes)
71 | #
72 | self.conv3 = conv_T(planes, planes, stride=1, padding=(1, 0, 0))
73 | self.bn3 = nn.BatchNorm3d(planes)
74 | else:
75 | self.conv_normal = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
76 | self.bn_normal = nn.BatchNorm2d(planes)
77 |
78 | if n_s < self.depth_3d:
79 | self.conv4 = nn.Conv3d(planes, planes * 4, kernel_size=1, bias=False)
80 | self.bn4 = nn.BatchNorm3d(planes * 4)
81 | else:
82 | self.conv4 = nn.Conv2d(planes, planes * 4, kernel_size=1, bias=False)
83 | self.bn4 = nn.BatchNorm2d(planes * 4)
84 | self.relu = nn.ReLU(inplace=True)
85 |
86 | self.stride = stride
87 |
88 | def ST_A(self, x):
89 | x = self.conv2(x)
90 | x = self.bn2(x)
91 | x = self.relu(x)
92 |
93 | x = self.conv3(x)
94 | x = self.bn3(x)
95 | x = self.relu(x)
96 |
97 | return x
98 |
99 | def ST_B(self, x):
100 | tmp_x = self.conv2(x)
101 | tmp_x = self.bn2(tmp_x)
102 | tmp_x = self.relu(tmp_x)
103 |
104 | x = self.conv3(x)
105 | x = self.bn3(x)
106 | x = self.relu(x)
107 |
108 | return x + tmp_x
109 |
110 | def ST_C(self, x):
111 | x = self.conv2(x)
112 | x = self.bn2(x)
113 | x = self.relu(x)
114 |
115 | tmp_x = self.conv3(x)
116 | tmp_x = self.bn3(tmp_x)
117 | tmp_x = self.relu(tmp_x)
118 |
119 | return x + tmp_x
120 |
121 | def forward(self, x):
122 | residual = x
123 |
124 | out = self.conv1(x)
125 | out = self.bn1(out)
126 | out = self.relu(out)
127 |
128 | # out = self.conv2(out)
129 | # out = self.bn2(out)
130 | # out = self.relu(out)
131 | if self.id < self.depth_3d: # C3D parts:
132 |
133 | if self.ST == 'A':
134 | out = self.ST_A(out)
135 | elif self.ST == 'B':
136 | out = self.ST_B(out)
137 | elif self.ST == 'C':
138 | out = self.ST_C(out)
139 | else:
140 | out = self.conv_normal(out) # normal is res5 part, C2D all.
141 | out = self.bn_normal(out)
142 | out = self.relu(out)
143 |
144 | out = self.conv4(out)
145 | out = self.bn4(out)
146 |
147 | if self.downsample is not None:
148 | residual = self.downsample(x)
149 |
150 | out += residual
151 | out = self.relu(out)
152 |
153 | return out
154 |
155 |
156 | class P3D(nn.Module):
157 |
158 | def __init__(self, block, layers, modality='RGB',
159 | shortcut_type='B', num_classes=400, dropout=0.5, ST_struc=('A', 'B', 'C')):
160 |
161 | self.inplanes = 64
162 | super(P3D, self).__init__()
163 | # self.conv1 = nn.Conv3d(3, 64, kernel_size=7, stride=(1, 2, 2),
164 | # padding=(3, 3, 3), bias=False)
165 | self.input_channel = 3 if modality == 'RGB' else 2 # 2 is for flow
166 | self.ST_struc = ST_struc
167 |
168 | self.conv1_custom = nn.Conv3d(self.input_channel, 64, kernel_size=(1, 7, 7), stride=(1, 2, 2),
169 | padding=(0, 3, 3), bias=False)
170 |
171 | self.depth_3d = sum(layers[:3]) # C3D layers are only (res2,res3,res4), res5 is C2D
172 |
173 | self.bn1 = nn.BatchNorm3d(64) # bn1 is followed by conv1
174 | self.cnt = 0
175 | self.relu = nn.ReLU(inplace=True)
176 | self.maxpool = nn.MaxPool3d(kernel_size=(2, 3, 3), stride=2, padding=(0, 1, 1)) # pooling layer for conv1.
177 | self.maxpool_2 = nn.MaxPool3d(kernel_size=(2, 1, 1), padding=0,
178 | stride=(2, 1, 1)) # pooling layer for res2, 3, 4.
179 |
180 | self.layer1 = self._make_layer(block, 64, layers[0], shortcut_type)
181 | self.layer2 = self._make_layer(block, 128, layers[1], shortcut_type, stride=2)
182 | self.layer3 = self._make_layer(block, 256, layers[2], shortcut_type, stride=2)
183 | self.layer4 = self._make_layer(block, 512, layers[3], shortcut_type, stride=2)
184 |
185 | self.avgpool = nn.AvgPool2d(kernel_size=(5, 5), stride=1) # pooling layer for res5.
186 | self.dropout = nn.Dropout(p=dropout)
187 | self.fc = nn.Linear(512 * block.expansion, num_classes)
188 |
189 | for m in self.modules():
190 | if isinstance(m, nn.Conv3d):
191 | n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
192 | m.weight.data.normal_(0, math.sqrt(2. / n))
193 | elif isinstance(m, nn.BatchNorm3d):
194 | m.weight.data.fill_(1)
195 | m.bias.data.zero_()
196 |
197 | # some private attribute
198 | self.input_size = (self.input_channel, 16, 160, 160) # input of the network
199 | self.input_mean = [0.485, 0.456, 0.406] if modality == 'RGB' else [0.5]
200 | self.input_std = [0.229, 0.224, 0.225] if modality == 'RGB' else [np.mean([0.229, 0.224, 0.225])]
201 |
202 | @property
203 | def scale_size(self):
204 | return self.input_size[2] * 256 // 160 # asume that raw images are resized (340,256).
205 |
206 | @property
207 | def temporal_length(self):
208 | return self.input_size[1]
209 |
210 | @property
211 | def crop_size(self):
212 | return self.input_size[2]
213 |
214 | def _make_layer(self, block, planes, blocks, shortcut_type, stride=1):
215 | downsample = None
216 | stride_p = stride # especially for downsample branch.
217 |
218 | if self.cnt < self.depth_3d:
219 | if self.cnt == 0:
220 | stride_p = 1
221 | else:
222 | stride_p = (1, 2, 2)
223 | if stride != 1 or self.inplanes != planes * block.expansion:
224 | if shortcut_type == 'A':
225 | downsample = partial(downsample_basic_block,
226 | planes=planes * block.expansion,
227 | stride=stride)
228 | else:
229 | downsample = nn.Sequential(
230 | nn.Conv3d(self.inplanes, planes * block.expansion,
231 | kernel_size=1, stride=stride_p, bias=False),
232 | nn.BatchNorm3d(planes * block.expansion)
233 | )
234 |
235 | else:
236 | if stride != 1 or self.inplanes != planes * block.expansion:
237 | if shortcut_type == 'A':
238 | downsample = partial(downsample_basic_block,
239 | planes=planes * block.expansion,
240 | stride=stride)
241 | else:
242 | downsample = nn.Sequential(
243 | nn.Conv2d(self.inplanes, planes * block.expansion,
244 | kernel_size=1, stride=2, bias=False),
245 | nn.BatchNorm2d(planes * block.expansion)
246 | )
247 | layers = []
248 | layers.append(block(self.inplanes, planes, stride, downsample, n_s=self.cnt, depth_3d=self.depth_3d,
249 | ST_struc=self.ST_struc))
250 | self.cnt += 1
251 |
252 | self.inplanes = planes * block.expansion
253 | for i in range(1, blocks):
254 | layers.append(block(self.inplanes, planes, n_s=self.cnt, depth_3d=self.depth_3d, ST_struc=self.ST_struc))
255 | self.cnt += 1
256 |
257 | return nn.Sequential(*layers)
258 |
259 | def forward(self, x):
260 | x = self.conv1_custom(x)
261 | x = self.bn1(x)
262 | x = self.relu(x)
263 | x = self.maxpool(x)
264 |
265 | x = self.maxpool_2(self.layer1(x)) # Part Res2
266 | x = self.maxpool_2(self.layer2(x)) # Part Res3
267 | x = self.maxpool_2(self.layer3(x)) # Part Res4
268 |
269 | sizes = x.size()
270 | x = x.view(-1, sizes[1], sizes[3], sizes[4]) # Part Res5
271 | x = self.layer4(x)
272 | x = self.avgpool(x)
273 |
274 | x = x.view(-1, self.fc.in_features)
275 | x = self.fc(self.dropout(x))
276 |
277 | return x
278 |
279 |
280 | def P3D63(**kwargs):
281 | """Construct a P3D63 modelbased on a ResNet-50-3D model.
282 | """
283 | model = P3D(Bottleneck, [3, 4, 6, 3], **kwargs)
284 | return model
285 |
286 |
287 | def P3D131(**kwargs):
288 | """Construct a P3D131 model based on a ResNet-101-3D model.
289 | """
290 | model = P3D(Bottleneck, [3, 4, 23, 3], **kwargs)
291 | return model
292 |
293 |
294 | def P3D199(pretrained=False, modality='RGB', **kwargs):
295 | """construct a P3D199 model based on a ResNet-152-3D model.
296 | """
297 | model = P3D(Bottleneck, [3, 8, 36, 3], modality=modality, **kwargs)
298 | if pretrained == True:
299 | if modality == 'RGB':
300 | pretrained_file = 'p3d_rgb_199.checkpoint.pth.tar'
301 | elif modality == 'Flow':
302 | pretrained_file = 'p3d_flow_199.checkpoint.pth.tar'
303 | weights = torch.load(pretrained_file)['state_dict']
304 | print('loading trained model')
305 | model.load_state_dict(weights)
306 | return model
307 |
308 |
309 | # custom operation
310 | def get_optim_policies(model=None, modality='RGB', enable_pbn=True):
311 | '''
312 | first conv: weight --> conv weight
313 | bias --> conv bias
314 | normal action: weight --> non-first conv + fc weight
315 | bias --> non-first conv + fc bias
316 | bn: the first bn2, and many all bn3.
317 |
318 | '''
319 | first_conv_weight = []
320 | first_conv_bias = []
321 | normal_weight = []
322 | normal_bias = []
323 | bn = []
324 |
325 | if model == None:
326 | logging.info('no model!')
327 | exit()
328 |
329 | conv_cnt = 0
330 | bn_cnt = 0
331 | for m in model.modules():
332 | if isinstance(m, torch.nn.Conv3d) or isinstance(m, torch.nn.Conv2d):
333 | ps = list(m.parameters())
334 | conv_cnt += 1
335 | if conv_cnt == 1:
336 | first_conv_weight.append(ps[0])
337 | if len(ps) == 2:
338 | first_conv_bias.append(ps[1])
339 | else:
340 | normal_weight.append(ps[0])
341 | if len(ps) == 2:
342 | normal_bias.append(ps[1])
343 | elif isinstance(m, torch.nn.Linear):
344 | ps = list(m.parameters())
345 | normal_weight.append(ps[0])
346 | if len(ps) == 2:
347 | normal_bias.append(ps[1])
348 |
349 | elif isinstance(m, torch.nn.BatchNorm3d):
350 | bn_cnt += 1
351 | # later BN's are frozen
352 | if not enable_pbn or bn_cnt == 1:
353 | bn.extend(list(m.parameters()))
354 | elif isinstance(m, torch.nn.BatchNorm2d):
355 | bn.extend(list(m.parameters()))
356 | elif len(m._modules) == 0:
357 | if len(list(m.parameters())) > 0:
358 | raise ValueError("New atomic module type: {}. Need to give it a learning policy".format(type(m)))
359 |
360 | slow_rate = 0.7
361 | n_fore = int(len(normal_weight) * slow_rate)
362 | slow_feat = normal_weight[:n_fore] # finetune slowly.
363 | slow_bias = normal_bias[:n_fore]
364 | normal_feat = normal_weight[n_fore:]
365 | normal_bias = normal_bias[n_fore:]
366 |
367 | return [
368 | {'params': first_conv_weight, 'lr_mult': 5 if modality == 'Flow' else 1, 'decay_mult': 1,
369 | 'name': "first_conv_weight"},
370 | {'params': first_conv_bias, 'lr_mult': 10 if modality == 'Flow' else 2, 'decay_mult': 0,
371 | 'name': "first_conv_bias"},
372 | {'params': slow_feat, 'lr_mult': 1, 'decay_mult': 1,
373 | 'name': "slow_feat"},
374 | {'params': slow_bias, 'lr_mult': 2, 'decay_mult': 0,
375 | 'name': "slow_bias"},
376 | {'params': normal_feat, 'lr_mult': 1, 'decay_mult': 1,
377 | 'name': "normal_feat"},
378 | {'params': normal_bias, 'lr_mult': 2, 'decay_mult': 0,
379 | 'name': "normal_bias"},
380 | {'params': bn, 'lr_mult': 1, 'decay_mult': 0,
381 | 'name': "BN scale/shift"},
382 | ]
383 |
384 |
385 | if __name__ == '__main__':
386 | model = P3D199(pretrained=True, num_classes=400)
387 | model = model.cuda()
388 | data = torch.autograd.Variable(
389 | torch.rand(10, 3, 16, 160, 160)).cuda() # if modality=='Flow', please change the 2nd dimension 3==>2
390 | out = model(data)
391 | print(out.size(), out)
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import os
3 | import os.path
4 | import time
5 | import torch.backends.cudnn as cudnn
6 | import torch.nn as nn
7 | import torch.nn.parallel
8 | import torch.optim as optim
9 | import torch.utils.data as data
10 | from meter import AverageMeter
11 |
12 | from logger import Logger
13 | # from video_transforms import *
14 | from transforms import *
15 | from Dataset import MyDataset
16 | from models.p3d_model import P3D199, get_optim_policies
17 | from models.C3D import C3D
18 | from models.i3dpt import I3D
19 | from utils import check_gpu, transfer_model, accuracy
20 |
21 | class Testing(object):
22 | def __init__(self, name_list, num_classes=400, modality='RGB', **kwargs):
23 | self.__dict__.update(kwargs)
24 |
25 | self.num_classes = num_classes
26 | self.modality = modality
27 | self.name_list = name_list
28 |
29 | # Set best precision = 0
30 | self.best_prec1 = 0
31 | # init start epoch = 0
32 | self.start_epoch = 0
33 |
34 | self.checkDataFolder()
35 |
36 | self.loading_model()
37 |
38 | self.test_loader = self.loading_data()
39 |
40 | # run
41 | self.process()
42 |
43 | def checkDataFolder(self):
44 | try:
45 | os.stat('./' + self.model_type + '_' + self.data_set)
46 | except:
47 | os.mkdir('./' + self.model_type + '_' + self.data_set)
48 | self.data_folder = './' + self.model_type + '_' + self.data_set
49 |
50 | # Loading P3D model
51 | def loading_model(self):
52 |
53 | print('Loading %s model' % (self.model_type))
54 | if self.model_type == 'C3D':
55 | self.model = C3D()
56 | elif self.model_type == 'I3D':
57 | self.model = I3D(num_classes=400, modality='rgb')
58 | else:
59 | self.model = P3D199(pretrained=False, num_classes=400, dropout=self.dropout)
60 |
61 |
62 | # Transfer classes
63 | self.model = transfer_model(model=self.model, model_type=self.model_type, num_classes=self.num_classes)
64 |
65 | # Check gpu and run parallel
66 | if check_gpu() > 0:
67 | self.model = torch.nn.DataParallel(self.model).cuda()
68 |
69 | # define loss function (criterion) and optimizer
70 | if check_gpu() > 0:
71 | self.criterion = nn.CrossEntropyLoss().cuda()
72 | else:
73 | self.criterion = nn.CrossEntropyLoss()
74 |
75 | policies = get_optim_policies(model=self.model, modality=self.modality, enable_pbn=True)
76 |
77 | self.optimizer = optim.SGD(policies, lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay)
78 |
79 | file = os.path.join(self.data_folder, 'model_best.pth.tar')
80 | if os.path.isfile(file):
81 | print("=> loading checkpoint '{}'".format('model_best.pth.tar'))
82 |
83 | checkpoint = torch.load(file)
84 | self.start_epoch = checkpoint['epoch']
85 | self.best_prec1 = checkpoint['best_prec1']
86 | self.model.load_state_dict(checkpoint['state_dict'])
87 | self.optimizer.load_state_dict(checkpoint['optimizer'])
88 | print("=> loaded model best ")
89 | else:
90 | print("=> no model best found at ")
91 | exit()
92 |
93 | cudnn.benchmark = True
94 |
95 | # Loading data
96 | def loading_data(self):
97 | size = 160
98 | if self.model_type == 'C3D':
99 | size = 112
100 | if self.model_type == 'I3D':
101 | size = 224
102 | normalize = Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
103 |
104 | val_transformations = Compose([
105 | Resize((size, size)),
106 | ToTensor(),
107 | normalize
108 | ])
109 |
110 | test_dataset = MyDataset(
111 | self.data,
112 | name_list=self.name_list,
113 | data_folder="test",
114 | version="1",
115 | transform=val_transformations,
116 | num_frames=self.num_frames
117 | )
118 |
119 | test_loader = data.DataLoader(
120 | test_dataset,
121 | batch_size=self.batch_size,
122 | shuffle=False,
123 | num_workers=self.workers,
124 | pin_memory=False)
125 |
126 | return test_loader
127 |
128 | # Test
129 | def process(self):
130 | acc = AverageMeter()
131 | top1 = AverageMeter()
132 | top5 = AverageMeter()
133 | losses = AverageMeter()
134 | log_file = os.path.join(self.data_folder, 'test.log')
135 | logger = Logger('test', log_file)
136 | # switch to evaluate mode
137 | self.model.eval()
138 |
139 | start_time = time.clock()
140 | print("Begin testing")
141 | for i, (images, labels) in enumerate(self.test_loader):
142 | if check_gpu() > 0:
143 | images = images.cuda(async=True)
144 | labels = labels.cuda(async=True)
145 |
146 | image_var = torch.autograd.Variable(images)
147 | label_var = torch.autograd.Variable(labels)
148 |
149 | # compute y_pred
150 | y_pred = self.model(image_var)
151 | loss = self.criterion(y_pred, label_var)
152 |
153 | # measure accuracy and record loss
154 | prec1, prec5 = accuracy(y_pred.data, labels, topk=(1, 5))
155 | losses.update(loss.item(), images.size(0))
156 | acc.update(prec1.item(), images.size(0))
157 | top1.update(prec1.item(), images.size(0))
158 | top5.update(prec5.item(), images.size(0))
159 |
160 | if i % self.print_freq == 0:
161 | print('TestVal: [{0}/{1}]\t'
162 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
163 | 'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
164 | 'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(
165 | i, len(self.test_loader), loss=losses, top1=top1, top5=top5))
166 |
167 | print(
168 | ' * Accuracy {acc.avg:.3f} Acc@5 {top5.avg:.3f} Loss {loss.avg:.3f}'.format(acc=acc, top5=top5,
169 | loss=losses))
170 |
171 | end_time = time.clock()
172 | print("Total testing time %.2gs" % (end_time - start_time))
173 | logger.info("Total testing time %.2gs" % (end_time - start_time))
174 | logger.info(
175 | ' * Accuracy {acc.avg:.3f} Acc@5 {top5.avg:.3f} Loss {loss.avg:.3f}'.format(acc=acc, top5=top5,
176 | loss=losses))
177 |
178 |
179 |
180 |
181 |
--------------------------------------------------------------------------------
/train.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 | import os
3 | import os.path
4 | import shutil
5 | import time
6 | import torch.backends.cudnn as cudnn
7 | import torch.nn as nn
8 | import torch.nn.parallel
9 | import torch.optim as optim
10 | import torch.utils.data as data
11 | from meter import AverageMeter
12 | from logger import Logger
13 | # from video_transforms import *
14 | from transforms import *
15 | from Dataset import MyDataset
16 | from models.p3d_model import P3D199, get_optim_policies
17 | from models.C3D import C3D
18 | from models.i3dpt import I3D
19 | from utils import check_gpu, transfer_model, accuracy, get_learning_rate
20 | from visualize import Visualizer
21 | from lr_scheduler import CyclicLR
22 |
23 | class Training(object):
24 | def __init__(self, name_list, num_classes=400, modality='RGB', **kwargs):
25 | self.__dict__.update(kwargs)
26 | self.num_classes = num_classes
27 | self.modality = modality
28 | self.name_list = name_list
29 | # set accuracy avg = 0
30 | self.count_early_stop = 0
31 | # Set best precision = 0
32 | self.best_prec1 = 0
33 | # init start epoch = 0
34 | self.start_epoch = 0
35 |
36 | if self.log_visualize != '':
37 | self.visualizer = Visualizer(logdir=self.log_visualize)
38 |
39 | self.checkDataFolder()
40 |
41 | self.loading_model()
42 |
43 | self.train_loader, self.val_loader = self.loading_data()
44 |
45 | # run
46 | self.processing()
47 | if self.random:
48 | print('random pick images')
49 |
50 | def check_early_stop(self, accuracy, logger, start_time):
51 | if self.best_prec1 <= accuracy:
52 | self.count_early_stop = 0
53 | else:
54 | self.count_early_stop += 1
55 |
56 | if self.count_early_stop > self.early_stop:
57 | print('Early stop')
58 | end_time = time.time()
59 | print("--- Total training time %s seconds ---" %
60 | (end_time - start_time))
61 | logger.info("--- Total training time %s seconds ---" %
62 | (end_time - start_time))
63 | exit()
64 |
65 | def checkDataFolder(self):
66 | try:
67 | os.stat('./' + self.model_type + '_' + self.data_set)
68 | except:
69 | os.mkdir('./' + self.model_type + '_' + self.data_set)
70 | self.data_folder = './' + self.model_type + '_' + self.data_set
71 |
72 | # Loading P3D model
73 | def loading_model(self):
74 |
75 | print('Loading %s model' % (self.model_type))
76 |
77 | if self.model_type == 'C3D':
78 | self.model = C3D()
79 | if self.pretrained:
80 | self.model.load_state_dict(torch.load('c3d.pickle'))
81 | elif self.model_type == 'I3D':
82 | if self.pretrained:
83 | self.model = I3D(num_classes=400, modality='rgb')
84 | self.model.load_state_dict(
85 | torch.load('kinetics_i3d_model_rgb.pth'))
86 | else:
87 | self.model = I3D(num_classes=self.num_classes, modality='rgb')
88 | else:
89 | if self.pretrained:
90 | print("=> using pre-trained model")
91 | self.model = P3D199(
92 | pretrained=True, num_classes=400, dropout=self.dropout)
93 |
94 | else:
95 | print("=> creating model P3D")
96 | self.model = P3D199(
97 | pretrained=False, num_classes=400, dropout=self.dropout)
98 | # Transfer classes
99 | self.model = transfer_model(model=self.model, model_type=self.model_type, num_classes=self.num_classes)
100 |
101 | # Check gpu and run parallel
102 | if check_gpu() > 0:
103 | self.model = torch.nn.DataParallel(self.model).cuda()
104 |
105 | # define loss function (criterion) and optimizer
106 | self.criterion = nn.CrossEntropyLoss()
107 |
108 | if check_gpu() > 0:
109 | self.criterion = nn.CrossEntropyLoss().cuda()
110 |
111 | params = self.model.parameters()
112 | if self.model_type == 'P3D':
113 | params = get_optim_policies( model=self.model, modality=self.modality, enable_pbn=True)
114 |
115 | self.optimizer = optim.SGD(params=params, lr=self.lr, momentum=self.momentum, weight_decay=self.weight_decay)
116 |
117 | # self.scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer=self.optimizer, mode='min', patience=10, verbose=True)
118 |
119 | # optionally resume from a checkpoint
120 | if self.resume:
121 | if os.path.isfile(self.resume):
122 | print("=> loading checkpoint '{}'".format(self.resume))
123 | checkpoint = torch.load(self.resume)
124 | self.start_epoch = checkpoint['epoch']
125 | self.best_prec1 = checkpoint['best_prec1']
126 | self.model.load_state_dict(checkpoint['state_dict'])
127 | self.optimizer.load_state_dict(checkpoint['optimizer'])
128 | print("=> loaded checkpoint '{}' (epoch {})".format(
129 | self.evaluate, checkpoint['epoch']))
130 | else:
131 | print("=> no checkpoint found at '{}'".format(self.resume))
132 |
133 | if self.evaluate:
134 | file_model_best = os.path.join(
135 | self.data_folder, 'model_best.pth.tar')
136 | if os.path.isfile(file_model_best):
137 | print("=> loading checkpoint '{}'".format('model_best.pth.tar'))
138 | checkpoint = torch.load(file_model_best)
139 | self.start_epoch = checkpoint['epoch']
140 | self.best_prec1 = checkpoint['best_prec1']
141 | self.model.load_state_dict(checkpoint['state_dict'])
142 | self.optimizer.load_state_dict(checkpoint['optimizer'])
143 | print("=> loaded checkpoint '{}' (epoch {})".format(
144 | self.evaluate, checkpoint['epoch']))
145 | else:
146 | print("=> no checkpoint found at '{}'".format(self.resume))
147 |
148 | cudnn.benchmark = True
149 |
150 | # Loading data
151 | def loading_data(self):
152 | random = True if self.random else False
153 | size = 160
154 | if self.model_type == 'C3D':
155 | size = 112
156 | if self.model_type == 'I3D':
157 | size = 224
158 |
159 | normalize = Normalize(mean=[0.485, 0.456, 0.406], std=[
160 | 0.229, 0.224, 0.225])
161 | train_transformations = Compose([
162 | RandomSizedCrop(size),
163 | RandomHorizontalFlip(),
164 | # Resize((size, size)),
165 | # ColorJitter(
166 | # brightness=0.4,
167 | # contrast=0.4,
168 | # saturation=0.4,
169 | # ),
170 | ToTensor(),
171 | normalize])
172 |
173 | val_transformations = Compose([
174 | # Resize((182, 242)),
175 | Resize(256),
176 | CenterCrop(size),
177 | ToTensor(),
178 | normalize
179 | ])
180 |
181 | train_dataset = MyDataset(
182 | self.data,
183 | data_folder="train",
184 | name_list=self.name_list,
185 | version="1",
186 | transform=train_transformations,
187 | num_frames=self.num_frames,
188 | random=random
189 | )
190 |
191 | val_dataset = MyDataset(
192 | self.data,
193 | data_folder="validation",
194 | name_list=self.name_list,
195 | version="1",
196 | transform=val_transformations,
197 | num_frames=self.num_frames,
198 | random=random
199 | )
200 |
201 | train_loader = data.DataLoader(
202 | train_dataset,
203 | batch_size=self.batch_size,
204 | shuffle=True,
205 | num_workers=self.workers,
206 | pin_memory=True)
207 |
208 | val_loader = data.DataLoader(
209 | val_dataset,
210 | batch_size=self.batch_size,
211 | shuffle=False,
212 | num_workers=self.workers,
213 | pin_memory=False)
214 |
215 | return (train_loader, val_loader)
216 |
217 | def processing(self):
218 | log_file = os.path.join(self.data_folder, 'train.log')
219 |
220 | logger = Logger('train', log_file)
221 |
222 | iters = len(self.train_loader)
223 | step_size = iters * 2
224 | self.scheduler = CyclicLR(optimizer=self.optimizer, step_size=step_size, base_lr=self.lr)
225 |
226 | if self.evaluate:
227 | self.validate(logger)
228 | return
229 |
230 | iter_per_epoch = len(self.train_loader)
231 | logger.info('Iterations per epoch: {0}'.format(iter_per_epoch))
232 | print('Iterations per epoch: {0}'.format(iter_per_epoch))
233 |
234 | start_time = time.time()
235 |
236 | for epoch in range(self.start_epoch, self.epochs):
237 | # self.adjust_learning_rate(epoch)
238 |
239 | # train for one epoch
240 | train_losses, train_acc = self.train(logger, epoch)
241 |
242 | # evaluate on validation set
243 | with torch.no_grad():
244 | val_losses, val_acc = self.validate(logger)
245 |
246 | # self.scheduler.step(val_losses.avg)
247 | # log visualize
248 | info_acc = {'train_acc': train_acc.avg, 'val_acc': val_acc.avg}
249 | info_loss = {'train_loss': train_losses.avg, 'val_loss': val_losses.avg}
250 | self.visualizer.write_summary(info_acc, info_loss, epoch + 1)
251 |
252 | self.visualizer.write_histogram(model=self.model, step=epoch + 1)
253 |
254 | # remember best Accuracy and save checkpoint
255 | is_best = val_acc.avg > self.best_prec1
256 | self.best_prec1 = max(val_acc.avg, self.best_prec1)
257 | self.save_checkpoint({
258 | 'epoch': epoch + 1,
259 | 'state_dict': self.model.state_dict(),
260 | 'best_prec1': self.best_prec1,
261 | 'optimizer': self.optimizer.state_dict(),
262 | }, is_best)
263 |
264 | self.check_early_stop(val_acc.avg, logger, start_time)
265 |
266 | end_time = time.time()
267 | print("--- Total training time %s seconds ---" %
268 | (end_time - start_time))
269 | logger.info("--- Total training time %s seconds ---" %
270 | (end_time - start_time))
271 | self.visualizer.writer_close()
272 |
273 | # Training
274 | def train(self, logger, epoch):
275 | batch_time = AverageMeter()
276 | data_time = AverageMeter()
277 | losses = AverageMeter()
278 | acc = AverageMeter()
279 | top1 = AverageMeter()
280 | top5 = AverageMeter()
281 |
282 | rate = get_learning_rate(self.optimizer)[0]
283 | # switch to train mode
284 | self.model.train()
285 |
286 | end = time.time()
287 | for i, (images, target) in enumerate(self.train_loader):
288 | # adjust learning rate scheduler step
289 | self.scheduler.batch_step()
290 |
291 | # measure data loading time
292 | data_time.update(time.time() - end)
293 | if check_gpu() > 0:
294 | images = images.cuda(async=True)
295 | target = target.cuda(async=True)
296 | image_var = torch.autograd.Variable(images)
297 | label_var = torch.autograd.Variable(target)
298 |
299 | self.optimizer.zero_grad()
300 |
301 | # compute y_pred
302 | y_pred = self.model(image_var)
303 | if self.model_type == 'I3D':
304 | y_pred = y_pred[0]
305 |
306 | loss = self.criterion(y_pred, label_var)
307 | # measure accuracy and record loss
308 | prec1, prec5 = accuracy(y_pred.data, target, topk=(1, 5))
309 | losses.update(loss.item(), images.size(0))
310 | acc.update(prec1.item(), images.size(0))
311 | top1.update(prec1.item(), images.size(0))
312 | top5.update(prec5.item(), images.size(0))
313 | # compute gradient and do SGD step
314 |
315 | loss.backward()
316 | self.optimizer.step()
317 |
318 | # measure elapsed time
319 | batch_time.update(time.time() - end)
320 | end = time.time()
321 |
322 | if i % self.print_freq == 0:
323 | print('Epoch: [{0}/{1}][{2}/{3}]\t'
324 | 'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t'
325 | 'Data {data_time.val:.3f} ({data_time.avg:.3f})\t'
326 | 'Lr {rate:.5f}\t'
327 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
328 | 'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
329 | 'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(epoch, self.epochs, i, len(self.train_loader),
330 | batch_time=batch_time, data_time=data_time,
331 | rate=rate,
332 | loss=losses, top1=top1, top5=top5))
333 |
334 | logger.info('Epoch: [{0}/{1}]\t'
335 | 'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t'
336 | 'Data {data_time.val:.3f} ({data_time.avg:.3f})\t'
337 | 'Lr {rate:.5f}\t'
338 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
339 | 'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
340 | 'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(epoch, self.epochs, batch_time=batch_time,
341 | data_time=data_time, rate=rate, loss=losses,
342 | top1=top1,
343 | top5=top5))
344 | return losses, acc
345 |
346 | # Validation
347 | def validate(self, logger):
348 | batch_time = AverageMeter()
349 | losses = AverageMeter()
350 | acc = AverageMeter()
351 | top1 = AverageMeter()
352 | top5 = AverageMeter()
353 | # switch to evaluate mode
354 | self.model.eval()
355 |
356 | end = time.time()
357 | for i, (images, labels) in enumerate(self.val_loader):
358 | if check_gpu() > 0:
359 | images = images.cuda(async=True)
360 | labels = labels.cuda(async=True)
361 |
362 | image_var = torch.autograd.Variable(images)
363 | label_var = torch.autograd.Variable(labels)
364 |
365 | # compute y_pred
366 | y_pred = self.model(image_var)
367 | if self.model_type == 'I3D':
368 | y_pred = y_pred[0]
369 |
370 | loss = self.criterion(y_pred, label_var)
371 |
372 | # measure accuracy and record loss
373 | prec1, prec5 = accuracy(y_pred.data, labels, topk=(1, 5))
374 | losses.update(loss.item(), images.size(0))
375 | acc.update(prec1.item(), images.size(0))
376 | top1.update(prec1.item(), images.size(0))
377 | top5.update(prec5.item(), images.size(0))
378 | # measure elapsed time
379 | batch_time.update(time.time() - end)
380 | end = time.time()
381 |
382 | if i % self.print_freq == 0:
383 | print('TrainVal: [{0}/{1}]\t'
384 | 'Time {batch_time.val:.3f} ({batch_time.avg:.3f})\t'
385 | 'Loss {loss.val:.4f} ({loss.avg:.4f})\t'
386 | 'Prec@1 {top1.val:.3f} ({top1.avg:.3f})\t'
387 | 'Prec@5 {top5.val:.3f} ({top5.avg:.3f})'.format(
388 | i, len(self.val_loader), batch_time=batch_time, loss=losses, top1=top1, top5=top5))
389 |
390 | print(
391 | ' * Accuracy {acc.avg:.3f} Loss {loss.avg:.3f}'.format(acc=acc, loss=losses))
392 | logger.info(
393 | ' * Accuracy {acc.avg:.3f} Loss {loss.avg:.3f}'.format(acc=acc, loss=losses))
394 |
395 | return losses, acc
396 |
397 | # save checkpoint to file
398 | def save_checkpoint(self, state, is_best):
399 | checkpoint = os.path.join(self.data_folder, 'checkpoint.pth.tar')
400 | torch.save(state, checkpoint)
401 | model_best = os.path.join(self.data_folder, 'model_best.pth.tar')
402 | if is_best:
403 | shutil.copyfile(checkpoint, model_best)
404 |
405 | # adjust learning rate for each epoch
406 | def adjust_learning_rate(self, epoch):
407 | """Sets the learning rate to the initial LR decayed by 10 every 3K iterations"""
408 | iters = len(self.train_loader)
409 | num_epochs = 3000 // iters
410 | decay = 0.1 ** (epoch // num_epochs)
411 | lr = self.lr * decay
412 | for param_group in self.optimizer.param_groups:
413 | param_group['lr'] = lr * param_group['lr_mult']
414 | param_group['weight_decay'] = decay * param_group['decay_mult']
415 |
--------------------------------------------------------------------------------
/transforms.py:
--------------------------------------------------------------------------------
1 | """
2 | https://github.com/yjxiong/tsn-pytorch/blob/master/transforms.py
3 | """
4 |
5 | import torchvision
6 | import random
7 | from PIL import Image, ImageOps
8 | import numpy as np
9 | import numbers
10 | import math
11 | import torch
12 |
13 |
14 | class Compose(object):
15 | def __init__(self, transforms):
16 | self.transforms = transforms
17 |
18 | def __call__(self, clip):
19 | for t in self.transforms:
20 | clip = t(clip)
21 | return clip
22 |
23 | class ColorJitter(object):
24 | def __init__(self, brightness=0, contrast=0, saturation=0, hue=0):
25 | self.worker = torchvision.transforms.ColorJitter(brightness, contrast, saturation, hue)
26 |
27 | def __call__(self, img_group):
28 | return [self.worker(img) for img in img_group]
29 |
30 |
31 | class RandomCrop(object):
32 | def __init__(self, size):
33 | if isinstance(size, numbers.Number):
34 | self.size = (int(size), int(size))
35 | else:
36 | self.size = size
37 |
38 | def __call__(self, img_group):
39 |
40 | w, h = img_group[0].size
41 | th, tw = self.size
42 |
43 | out_images = list()
44 |
45 | x1 = random.randint(0, w - tw)
46 | y1 = random.randint(0, h - th)
47 |
48 | for img in img_group:
49 | assert (img.size[0] == w and img.size[1] == h)
50 | if w == tw and h == th:
51 | out_images.append(img)
52 | else:
53 | out_images.append(img.crop((x1, y1, x1 + tw, y1 + th)))
54 |
55 | return out_images
56 |
57 |
58 | class CenterCrop(object):
59 | def __init__(self, size):
60 | self.worker = torchvision.transforms.CenterCrop(size)
61 |
62 | def __call__(self, img_group):
63 | return [self.worker(img) for img in img_group]
64 |
65 |
66 | class RandomHorizontalFlip(object):
67 | """Randomly horizontally flips the given PIL.Image with a probability of 0.5
68 | """
69 |
70 | def __init__(self, is_flow=False):
71 | self.is_flow = is_flow
72 |
73 | def __call__(self, img_group, is_flow=False):
74 | v = random.random()
75 | if v < 0.5:
76 | ret = [img.transpose(Image.FLIP_LEFT_RIGHT) for img in img_group]
77 | if self.is_flow:
78 | for i in range(0, len(ret), 2):
79 | ret[i] = ImageOps.invert(ret[i]) # invert flow pixel values when flipping
80 | return ret
81 | else:
82 | return img_group
83 |
84 |
85 | class Normalize(object):
86 | def __init__(self, mean, std):
87 | self.mean = mean
88 | self.std = std
89 |
90 | def __call__(self, tensor):
91 | rep_mean = self.mean * (tensor.size()[0] // len(self.mean))
92 | rep_std = self.std * (tensor.size()[0] // len(self.std))
93 | # TODO: make efficient
94 | for t, m, s in zip(tensor, rep_mean, rep_std):
95 | t.sub_(m).div_(s)
96 | return tensor
97 |
98 |
99 | class Scale(object):
100 | """ Rescales the input PIL.Image to the given 'size'.
101 | 'size' will be the size of the smaller edge.
102 | For example, if height > width, then image will be
103 | rescaled to (size * height / width, size)
104 | size: size of the smaller edge
105 | interpolation: Default: PIL.Image.BILINEAR
106 | """
107 |
108 | def __init__(self, size, interpolation=Image.BILINEAR):
109 | self.worker = torchvision.transforms.Scale(size, interpolation)
110 |
111 | def __call__(self, img_group):
112 | return [self.worker(img) for img in img_group]
113 |
114 |
115 | class Resize(object):
116 | def __init__(self, size, interpolation=Image.BILINEAR):
117 | self.worker = torchvision.transforms.Resize(size, interpolation)
118 |
119 | def __call__(self, img_group):
120 | return [self.worker(img) for img in img_group]
121 |
122 |
123 | class OverSample(object):
124 | def __init__(self, crop_size, scale_size=None):
125 | self.crop_size = crop_size if not isinstance(crop_size, int) else (crop_size, crop_size)
126 |
127 | if scale_size is not None:
128 | self.scale_worker = Scale(scale_size)
129 | else:
130 | self.scale_worker = None
131 |
132 | def __call__(self, img_group):
133 |
134 | if self.scale_worker is not None:
135 | img_group = self.scale_worker(img_group)
136 |
137 | image_w, image_h = img_group[0].size
138 | crop_w, crop_h = self.crop_size
139 |
140 | offsets = MultiScaleCrop.fill_fix_offset(False, image_w, image_h, crop_w, crop_h)
141 | oversample_group = list()
142 | for o_w, o_h in offsets:
143 | normal_group = list()
144 | flip_group = list()
145 | for i, img in enumerate(img_group):
146 | crop = img.crop((o_w, o_h, o_w + crop_w, o_h + crop_h))
147 | normal_group.append(crop)
148 | flip_crop = crop.copy().transpose(Image.FLIP_LEFT_RIGHT)
149 |
150 | if img.mode == 'L' and i % 2 == 0:
151 | flip_group.append(ImageOps.invert(flip_crop))
152 | else:
153 | flip_group.append(flip_crop)
154 |
155 | oversample_group.extend(normal_group)
156 | oversample_group.extend(flip_group)
157 | return oversample_group
158 |
159 |
160 | class MultiScaleCrop(object):
161 |
162 | def __init__(self, input_size, scales=None, max_distort=1, fix_crop=True, more_fix_crop=True):
163 | self.scales = scales if scales is not None else [1, .875, .75, .66]
164 | self.max_distort = max_distort
165 | self.fix_crop = fix_crop
166 | self.more_fix_crop = more_fix_crop
167 | self.input_size = input_size if not isinstance(input_size, int) else [input_size, input_size]
168 | self.interpolation = Image.BILINEAR
169 |
170 | def __call__(self, img_group):
171 |
172 | im_size = img_group[0].size
173 |
174 | crop_w, crop_h, offset_w, offset_h = self._sample_crop_size(im_size)
175 | crop_img_group = [img.crop((offset_w, offset_h, offset_w + crop_w, offset_h + crop_h)) for img in img_group]
176 | ret_img_group = [img.resize((self.input_size[0], self.input_size[1]), self.interpolation)
177 | for img in crop_img_group]
178 | return ret_img_group
179 |
180 | def _sample_crop_size(self, im_size):
181 | image_w, image_h = im_size[0], im_size[1]
182 |
183 | # find a crop size
184 | base_size = min(image_w, image_h)
185 | crop_sizes = [int(base_size * x) for x in self.scales]
186 | crop_h = [self.input_size[1] if abs(x - self.input_size[1]) < 3 else x for x in crop_sizes]
187 | crop_w = [self.input_size[0] if abs(x - self.input_size[0]) < 3 else x for x in crop_sizes]
188 |
189 | pairs = []
190 | for i, h in enumerate(crop_h):
191 | for j, w in enumerate(crop_w):
192 | if abs(i - j) <= self.max_distort:
193 | pairs.append((w, h))
194 |
195 | crop_pair = random.choice(pairs)
196 | if not self.fix_crop:
197 | w_offset = random.randint(0, image_w - crop_pair[0])
198 | h_offset = random.randint(0, image_h - crop_pair[1])
199 | else:
200 | w_offset, h_offset = self._sample_fix_offset(image_w, image_h, crop_pair[0], crop_pair[1])
201 |
202 | return crop_pair[0], crop_pair[1], w_offset, h_offset
203 |
204 | def _sample_fix_offset(self, image_w, image_h, crop_w, crop_h):
205 | offsets = self.fill_fix_offset(self.more_fix_crop, image_w, image_h, crop_w, crop_h)
206 | return random.choice(offsets)
207 |
208 | @staticmethod
209 | def fill_fix_offset(more_fix_crop, image_w, image_h, crop_w, crop_h):
210 | w_step = (image_w - crop_w) // 4
211 | h_step = (image_h - crop_h) // 4
212 |
213 | ret = list()
214 | ret.append((0, 0)) # upper left
215 | ret.append((4 * w_step, 0)) # upper right
216 | ret.append((0, 4 * h_step)) # lower left
217 | ret.append((4 * w_step, 4 * h_step)) # lower right
218 | ret.append((2 * w_step, 2 * h_step)) # center
219 |
220 | if more_fix_crop:
221 | ret.append((0, 2 * h_step)) # center left
222 | ret.append((4 * w_step, 2 * h_step)) # center right
223 | ret.append((2 * w_step, 4 * h_step)) # lower center
224 | ret.append((2 * w_step, 0 * h_step)) # upper center
225 |
226 | ret.append((1 * w_step, 1 * h_step)) # upper left quarter
227 | ret.append((3 * w_step, 1 * h_step)) # upper right quarter
228 | ret.append((1 * w_step, 3 * h_step)) # lower left quarter
229 | ret.append((3 * w_step, 3 * h_step)) # lower righ quarter
230 |
231 | return ret
232 |
233 |
234 | class RandomSizedCrop(object):
235 | """Random crop the given PIL.Image to a random size of (0.08 to 1.0) of the original size
236 | and and a random aspect ratio of 3/4 to 4/3 of the original aspect ratio
237 | This is popularly used to train the Inception networks
238 | size: size of the smaller edge
239 | interpolation: Default: PIL.Image.BILINEAR
240 | """
241 |
242 | def __init__(self, size, interpolation=Image.BILINEAR):
243 | self.size = size
244 | self.interpolation = interpolation
245 |
246 | def __call__(self, img_group):
247 | for attempt in range(10):
248 | area = img_group[0].size[0] * img_group[0].size[1]
249 | target_area = random.uniform(0.08, 1.0) * area
250 | aspect_ratio = random.uniform(3. / 4, 4. / 3)
251 |
252 | w = int(round(math.sqrt(target_area * aspect_ratio)))
253 | h = int(round(math.sqrt(target_area / aspect_ratio)))
254 |
255 | if random.random() < 0.5:
256 | w, h = h, w
257 |
258 | if w <= img_group[0].size[0] and h <= img_group[0].size[1]:
259 | x1 = random.randint(0, img_group[0].size[0] - w)
260 | y1 = random.randint(0, img_group[0].size[1] - h)
261 | found = True
262 | break
263 | else:
264 | found = False
265 | x1 = 0
266 | y1 = 0
267 |
268 | if found:
269 | out_group = list()
270 | for img in img_group:
271 | img = img.crop((x1, y1, x1 + w, y1 + h))
272 | assert (img.size == (w, h))
273 | out_group.append(img.resize((self.size, self.size), self.interpolation))
274 | return out_group
275 | else:
276 | # Fallback
277 | scale = Scale(self.size, interpolation=self.interpolation)
278 | crop = RandomCrop(self.size)
279 | return crop(scale(img_group))
280 |
281 |
282 | class Stack(object):
283 |
284 | def __init__(self, roll=False):
285 | self.roll = roll
286 |
287 | def __call__(self, img_group):
288 | if img_group[0].mode == 'L':
289 | return np.concatenate([np.expand_dims(x, 2) for i, x in enumerate(img_group)], axis=2)
290 | elif img_group[0].mode == 'RGB':
291 | if self.roll:
292 | return np.concatenate([np.array(x)[:, :, ::-1] for i, x in enumerate(img_group)], axis=2)
293 | else:
294 | return np.concatenate(img_group, axis=2)
295 |
296 |
297 | # class ToTensor2(object):
298 | # """ Converts a PIL.Image (RGB) or numpy.ndarray (H x W x C) in the range [0, 255]
299 | # to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0] """
300 | #
301 | # def __init__(self, channel_nb=3, div=True, numpy=False):
302 | # self.div = div
303 | # self.channel_nb=channel_nb
304 | # self.numpy = numpy
305 | #
306 | # def __call__(self, pic):
307 | # if isinstance(pic, np.ndarray):
308 | # # handle numpy array
309 | # img = torch.from_numpy(pic).permute(2, 0, 1).contiguous()
310 | # # print(img.size())
311 | # else:
312 | # # handle PIL Image
313 | # img = torch.ByteTensor(torch.ByteStorage.from_buffer(pic.tobytes()))
314 | # img = img.view(pic.size[1], pic.size[0], len(pic.mode))
315 | # # put it from HWC to CHW format
316 | # # yikes, this transpose takes 80% of the loading time/CPU
317 | # img = img.transpose(0, 1).transpose(0, 2).contiguous()
318 | #
319 | # return img.float().div(255) if self.div else img.float()
320 |
321 | class ToTensor(object):
322 | """Convert a list of m (H x W x C) numpy.ndarrays in the range [0, 255]
323 | to a torch.FloatTensor of shape (C x m x H x W) in the range [0, 1.0]
324 | """
325 |
326 | def __init__(self, channel_nb=3, div_255=True, numpy=False):
327 | self.channel_nb = channel_nb
328 | self.div_255 = div_255
329 | self.numpy = numpy
330 |
331 | def convert_img(self, img):
332 | """Converts (H, W, C) numpy.ndarray to (C, W, H) format
333 | """
334 | if len(img.shape) == 3:
335 | img = img.transpose(2, 0, 1)
336 | if len(img.shape) == 2:
337 | img = np.expand_dims(img, 0)
338 | return img
339 |
340 | def __call__(self, clip):
341 | """
342 | Args: clip (list of numpy.ndarray): clip (list of images)
343 | to be converted to tensor.
344 | """
345 |
346 | h, w = clip[0].size
347 | np_clip = np.zeros([self.channel_nb, len(clip), int(h), int(w)])
348 |
349 | # Convert
350 | for img_idx, img in enumerate(clip):
351 | if isinstance(img, np.ndarray):
352 | pass
353 | elif isinstance(img, Image.Image):
354 | img = np.array(img, copy=False)
355 | else:
356 | raise TypeError('Expected numpy.ndarray or PIL.Image\
357 | but got list of {0}'.format(type(clip[0])))
358 | img = self.convert_img(img)
359 | np_clip[:, img_idx, :, :] = img
360 | if self.numpy:
361 | if self.div_255:
362 | np_clip = np_clip / 255
363 | return np_clip
364 |
365 | else:
366 | tensor_clip = torch.from_numpy(np_clip)
367 |
368 | if not isinstance(tensor_clip, torch.FloatTensor):
369 | tensor_clip = tensor_clip.float()
370 | if self.div_255:
371 | tensor_clip = tensor_clip.div(255)
372 | return tensor_clip
373 |
374 | class IdentityTransform(object):
375 |
376 | def __call__(self, data):
377 | return data
378 |
379 |
380 | if __name__ == "__main__":
381 | trans = Compose([
382 | Scale(256),
383 | RandomCrop(224),
384 | Stack(),
385 | ToTensor(),
386 | Normalize(
387 | mean=[.485, .456, .406],
388 | std=[.229, .224, .225]
389 | )]
390 | )
391 |
392 | im = Image.open('dog.0.jpg')
393 |
394 | color_group = [im] * 3
395 | rst = trans(color_group)
396 |
397 | gray_group = [im.convert('L')] * 9
398 | gray_rst = trans(gray_group)
399 |
400 | trans2 = torchvision.transforms.Compose([
401 | RandomSizedCrop(256),
402 | Stack(),
403 | ToTensor(),
404 | Normalize(
405 | mean=[.485, .456, .406],
406 | std=[.229, .224, .225])
407 | ])
408 | print(trans2(color_group))
409 |
--------------------------------------------------------------------------------
/utils.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torch.nn as nn
3 | from models.i3dpt import Unit3Dpy, I3D
4 |
5 |
6 | # get accuracy from y pred
7 | def accuracy(y_pred, y_actual, topk=(1,)):
8 | """Computes the precision@k for the specified values of k"""
9 | maxk = max(topk)
10 | batch_size = y_actual.size(0)
11 |
12 | _, pred = y_pred.topk(maxk, 1, True, True)
13 | pred = pred.t()
14 | correct = pred.eq(y_actual.view(1, -1).expand_as(pred))
15 |
16 | res = []
17 | for k in topk:
18 | correct_k = correct[:k].view(-1).float().sum(0)
19 | res.append(correct_k.mul_(100.0 / batch_size))
20 |
21 | return res
22 |
23 |
24 | def check_gpu():
25 | num_gpus = 0
26 | if torch.cuda.is_available():
27 | num_gpus = torch.cuda.device_count()
28 | return num_gpus
29 |
30 |
31 | def transfer_model(model, model_type='P3D', num_classes=101):
32 | if model_type == 'P3D':
33 | num_ftrs = model.fc.in_features
34 | model.fc = nn.Linear(num_ftrs, num_classes)
35 | elif model_type == 'C3D':
36 | model.fc8 = nn.Linear(4096, num_classes)
37 | elif model_type == "I3D":
38 | conv3d_0c_1x1 = Unit3Dpy(
39 | in_channels=1024,
40 | out_channels=num_classes,
41 | kernel_size=(1, 1, 1),
42 | activation=None,
43 | use_bias=True,
44 | use_bn=False)
45 | model.conv3d_0c_1x1 = conv3d_0c_1x1
46 | return model
47 |
48 |
49 | def get_learning_rate(optimizer):
50 | lr = []
51 | for param_group in optimizer.param_groups:
52 | lr += [param_group['lr']]
53 | return lr
--------------------------------------------------------------------------------
/video_transforms.py:
--------------------------------------------------------------------------------
1 | import torch
2 | import torchvision
3 | import math
4 | import random
5 | import numpy as np
6 | from PIL import Image, ImageOps, ImageEnhance
7 |
8 |
9 | class Compose(object):
10 | def __init__(self, transforms):
11 | self.transforms = transforms
12 |
13 | def __call__(self, clip):
14 | for t in self.transforms:
15 | clip = t(clip)
16 | return clip
17 |
18 |
19 | class Resize(object):
20 | def __init__(self, size, interpolation=Image.BILINEAR):
21 | self.worker = torchvision.transforms.Resize(size, interpolation)
22 |
23 | def __call__(self, img_group):
24 | return [self.worker(img) for img in img_group]
25 |
26 |
27 | class CenterCrop(object):
28 | def __init__(self, size):
29 | self.worker = torchvision.transforms.CenterCrop(size)
30 |
31 | def __call__(self, img_group):
32 | return [self.worker(img) for img in img_group]
33 |
34 |
35 | class RandomHorizontalFlip(object):
36 | def __init__(self, is_flow=False):
37 | self.is_flow = is_flow
38 |
39 | def __call__(self, img_group, is_flow=False):
40 | v = random.random()
41 | if v < 0.5:
42 | ret = [img.transpose(Image.FLIP_LEFT_RIGHT) for img in img_group]
43 | if self.is_flow:
44 | for i in range(0, len(ret), 2):
45 | ret[i] = ImageOps.invert(ret[i])
46 | return ret
47 | else:
48 | return img_group
49 |
50 |
51 | class RandomResizedCrop(object):
52 | def __init__(self, size, interpolation=Image.BILINEAR):
53 | self.size = size
54 | self.interpolation = interpolation
55 |
56 | def __call__(self, img_group):
57 | for attempt in range(10):
58 | area = img_group[0].size[0] * img_group[0].size[1]
59 | target_area = random.uniform(0.08, 1.0) * area
60 | aspect_ratio = random.uniform(3. / 4, 4. / 3)
61 |
62 | w = int(round(math.sqrt(target_area * aspect_ratio)))
63 | h = int(round(math.sqrt(target_area / aspect_ratio)))
64 |
65 | if random.random() < 0.5:
66 | w, h = h, w
67 |
68 | if w <= img_group[0].size[0] and h <= img_group[0].size[1]:
69 | x1 = random.randint(0, img_group[0].size[0] - w)
70 | y1 = random.randint(0, img_group[0].size[1] - h)
71 | found = True
72 | break
73 | else:
74 | found = False
75 | x1 = 0
76 | y1 = 0
77 |
78 | if found:
79 | out_group = list()
80 | for img in img_group:
81 | img = img.crop((x1, y1, x1 + w, y1 + h))
82 | assert (img.size == (w, h))
83 | out_group.append(img.resize((self.size, self.size), self.interpolation))
84 | return out_group
85 | else:
86 | scale = Resize(self.size, interpolation=self.interpolation)
87 | return scale(img_group)
88 |
89 |
90 | class Normalize(object):
91 | def __init__(self, mean, std, channel=3, num_frames=16, size=160):
92 | self.mean = mean
93 | self.std = std
94 | self.channel = channel
95 | self.num_frames = num_frames
96 | self.size = size
97 |
98 | def __call__(self, tensor):
99 | rep_mean = self.mean * (tensor.size()[1])
100 | rep_std = self.std * (tensor.size()[1])
101 | tensor = tensor.contiguous().view(self.channel * self.num_frames, self.size, self.size)
102 | for t, m, s in zip(tensor, rep_mean, rep_std):
103 | t.sub_(m).div_(s)
104 | tensor = tensor.view(self.channel, self.num_frames, self.size, self.size)
105 | return tensor
106 |
107 |
108 | class ToTensor(object):
109 | def __init__(self):
110 | self.worker = torchvision.transforms.ToTensor()
111 |
112 | def __call__(self, img_group):
113 | img_group = [self.worker(img) for img in img_group]
114 | img_group = [img.numpy() for img in img_group]
115 | img_group = np.array(img_group)
116 | img_group = img_group.transpose(1, 0, 2, 3)
117 | img_group = torch.from_numpy(img_group)
118 | return img_group
119 |
--------------------------------------------------------------------------------
/visualize.py:
--------------------------------------------------------------------------------
1 | from tensorboardX import SummaryWriter
2 |
3 | class Visualizer(object):
4 | def __init__(self, logdir='./runs'):
5 | self.writer = SummaryWriter(logdir)
6 |
7 | def write_graph(self, model, dummy_input):
8 | self.writer.add_graph(model, (dummy_input, ))
9 |
10 | def write_summary(self, info_acc, info_loss, epoch):
11 | self.writer.add_scalars('Accuracy', info_acc, epoch)
12 | self.writer.add_scalars('Loss', info_loss, epoch)
13 |
14 | def write_histogram(self, model, step):
15 | for name, param in model.named_parameters():
16 | self.writer.add_histogram(name, param.clone().cpu().data.numpy(), step)
17 |
18 | def writer_close(self):
19 | self.writer.export_scalars_to_json("./all_scalars.json")
20 | self.writer.close()
--------------------------------------------------------------------------------