File size: 4,991 Bytes
2e36228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import os
import random
import math


def temporal_batching_index(fr,length=16):
    '''
    Do padding or half-overlapping clips for video.
    
    Input:
        fr: number of frames
    Output:
        batch_indices: array for batch where each element is frame index 
    '''
    if fr < length: 
        #e.g. (1,2,3,4,5) to (1,1,....,1,2,3,4,5,5,...,5,5)
        right = int((length-fr)/2)
        left = length - right - fr
        return [[0]*left + list(range(fr)) + [fr-1]*right]
    
    batch_indices = []
    last_idx = fr - 1
    assert length%2 == 0
    half = int(length/2)
    for i in range(0,fr-half,half):
            frame_indices = [0,]*length
            for j in range(length):
                current_idx =  i + j 
                if current_idx < last_idx:
                    frame_indices[j] = current_idx
                else:
                    frame_indices[j] = last_idx
            batch_indices.append(frame_indices)
            
    return batch_indices

def temporal_sliding_window(clip,window = 16):
    '''
    Make a batched tensor with 16 frame sliding window with the overlap of 8. 
    If a clip is not the multiply of 8, it's padded with the last frames. (1,2...,13,14,14,14) for (1,..,14) 
    If a clip is less than 16 frames, padding is applied like (1,1,....,1,2,3,4,5,5,...,5,5) for (1,2,3,4,5)
    This can be used for sliding window evaluation.
    
    Input:  list of image paths
    Output: torch tensor of shape of (batch,ch,16,h,w).
    '''

    batch_indices = temporal_batching_index(len(clip),length = window)
    
    return [[clip[idx] for idx in  indices] for indices in batch_indices]

def temporal_center_crop(clip,length = 16):
    '''
    Input:  list of image paths
    Output: torch tensor of shape of (1,ch,16,h,w).
    '''
    fr = len(clip) 
    if fr < length: 
        #e.g. (1,2,3,4,5) to (1,1,....,1,2,3,4,5,5,...,5,5)
        right = int((length-fr)/2)
        left = length - right - fr
        indicies =  [0]*left + list(range(fr)) + [fr-1]*right
        output =  [clip[i] for i in indicies]
    elif fr==length:
        output =  clip    
    else:
        middle = int(fr/2)
        assert length%2 == 0
        half = int(length/2)
        start = middle - half
        output =  clip[start : start+length]
        
    return output[::2]



def random_temporal_crop(clip,length = 16):
    '''
    Just randomly sample 16 consecutive frames
    if less than 16 frames, just add padding.
    '''
    fr = len(clip) 
    if fr < length: 
        #e.g. (1,2,3,4,5) to (1,1,....,1,2,3,4,5,5,...,5,5)
        right = int((length-fr)/2)
        left = length - right - fr
        indicies =  [0]*left + list(range(fr)) + [fr-1]*right
        output =  [clip[i] for i in indicies]
    elif fr==length:
        output =  clip
    else:
        start=random.randint(0,fr-length)
        output =  clip[start : start+length]
    return output[::2]


def use_all_frames(clip):
    '''
    Just use it as it is :)
    '''
    return clip

def looppadding(clip, length=16):


        out = clip

        for index in out:
            if len(out) >= length:
                break
            out.append(index)

        return out[::2]

def temporal_even_crop(clip, length=16, n_samples=1):

        clip = list(clip)
        n_frames = len(clip)
        indices = list(range(len(clip)))
        stride = max(
            1, math.ceil((n_frames - 1 - length) / (n_samples - 1)))

        out = []
        for begin_index in indices[::stride]:
            if len(out) >= n_samples:
                break
            end_index = min(indices[-1] + 1, begin_index + length)
            sample = list(range(begin_index, end_index))

            if len(sample) < length:
                out.append([clip[i] for i in looppadding(sample, length=length)])
               # out.append(clip[looppadding(sample, length=length)])
                break
            else:
                out.append([clip[i] for i in sample[::2]])
               # out.append(clip[sample[::2]])

        return out


class TemporalTransform(object):
    def __init__(self,length,mode="center"):
        self.mode = mode
        self.length = length
        #pass dummpy in order to catch incoored mode
        self.__call__(range(128))
        
    def __call__(self, clip):
        if self.mode == "random":
            return random_temporal_crop(clip,self.length)
        elif self.mode == "center":
            return temporal_center_crop(clip,self.length)
        elif self.mode == "all" or self.mode == "nocrop":
            #note that length cannot be satisfied!
            return use_all_frames(clip)
        elif self.mode == "slide":
            #note that output has one more dimention
            return temporal_sliding_window(clip,self.length)
        elif self.mode == "even":
            return temporal_even_crop(clip, self.length, n_samples=5)
        else:
            raise NotImplementedError("this option is not defined:",self.mode)