| 5 | |
| 6 | |
| 7 | class Downsample3D(torch.nn.Module): |
| 8 | def __init__( |
| 9 | self, |
| 10 | in_channels: int, |
| 11 | out_channels: int, |
| 12 | kernel_size: int = 3, |
| 13 | stride: int = 2, |
| 14 | padding: int = 0, |
| 15 | compress_time: bool = False, |
| 16 | ): |
| 17 | super().__init__() |
| 18 | |
| 19 | self.conv = torch.nn.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding) |
| 20 | self.compress_time = compress_time |
| 21 | |
| 22 | def forward(self, x: torch.Tensor, xq: torch.Tensor) -> torch.Tensor: |
| 23 | if self.compress_time: |
| 24 | batch_size, channels, frames, height, width = x.shape |
| 25 | |
| 26 | # (batch_size, channels, frames, height, width) -> (batch_size, height, width, channels, frames) -> (batch_size * height * width, channels, frames) |
| 27 | x = x.permute(0, 3, 4, 1, 2).reshape(batch_size * height * width, channels, frames) |
| 28 | |
| 29 | if x.shape[-1] % 2 == 1: |
| 30 | x_first, x_rest = x[..., 0], x[..., 1:] |
| 31 | if x_rest.shape[-1] > 0: |
| 32 | # (batch_size * height * width, channels, frames - 1) -> (batch_size * height * width, channels, (frames - 1) // 2) |
| 33 | x_rest = torch.nn.functional.avg_pool1d(x_rest, kernel_size=2, stride=2) |
| 34 | |
| 35 | x = torch.cat([x_first[..., None], x_rest], dim=-1) |
| 36 | # (batch_size * height * width, channels, (frames // 2) + 1) -> (batch_size, height, width, channels, (frames // 2) + 1) -> (batch_size, channels, (frames // 2) + 1, height, width) |
| 37 | x = x.reshape(batch_size, height, width, channels, x.shape[-1]).permute(0, 3, 4, 1, 2) |
| 38 | else: |
| 39 | # (batch_size * height * width, channels, frames) -> (batch_size * height * width, channels, frames // 2) |
| 40 | x = torch.nn.functional.avg_pool1d(x, kernel_size=2, stride=2) |
| 41 | # (batch_size * height * width, channels, frames // 2) -> (batch_size, height, width, channels, frames // 2) -> (batch_size, channels, frames // 2, height, width) |
| 42 | x = x.reshape(batch_size, height, width, channels, x.shape[-1]).permute(0, 3, 4, 1, 2) |
| 43 | |
| 44 | # Pad the tensor |
| 45 | pad = (0, 1, 0, 1) |
| 46 | x = torch.nn.functional.pad(x, pad, mode="constant", value=0) |
| 47 | batch_size, channels, frames, height, width = x.shape |
| 48 | # (batch_size, channels, frames, height, width) -> (batch_size, frames, channels, height, width) -> (batch_size * frames, channels, height, width) |
| 49 | x = x.permute(0, 2, 1, 3, 4).reshape(batch_size * frames, channels, height, width) |
| 50 | x = self.conv(x) |
| 51 | # (batch_size * frames, channels, height, width) -> (batch_size, frames, channels, height, width) -> (batch_size, channels, frames, height, width) |
| 52 | x = x.reshape(batch_size, frames, x.shape[1], x.shape[2], x.shape[3]).permute(0, 2, 1, 3, 4) |
| 53 | return x |
| 54 | |
| 55 | |
| 56 | |