Download models/tmva_net_sv.py from PeCoLab/EchMo-Vis: direct link, hf CLI and curl.
- Browser
- Download file 16.4 kB
-
https://huggingface.co/PeCoLab/EchMo-Vis/resolve/main/models/tmva_net_sv.py
- Command line
-
hf download hf://PeCoLab/EchMo-Vis/models/tmva_net_sv.py
-
curl -L -o tmva_net_sv.py https://huggingface.co/PeCoLab/EchMo-Vis/resolve/main/models/tmva_net_sv.py
16.4 kB
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| class DoubleConvBlock(nn.Module): | |
| """ (2D conv => BN => LeakyReLU) * 2 """ | |
| def __init__(self, in_ch, out_ch, k_size, pad, dil): | |
| super().__init__() | |
| self.block = nn.Sequential( | |
| nn.Conv2d(in_ch, out_ch, kernel_size=k_size, padding=pad, dilation=dil), | |
| nn.BatchNorm2d(out_ch), | |
| nn.LeakyReLU(inplace=True), | |
| nn.Conv2d(out_ch, out_ch, kernel_size=k_size, padding=pad, dilation=dil), | |
| nn.BatchNorm2d(out_ch), | |
| nn.LeakyReLU(inplace=True) | |
| ) | |
| def forward(self, x): | |
| x = self.block(x) | |
| return x | |
| class Double3DConvBlock(nn.Module): | |
| """ (3D conv => BN => LeakyReLU) * 2 """ | |
| def __init__(self, in_ch, out_ch, k_size, pad, dil): | |
| super().__init__() | |
| self.block = nn.Sequential( | |
| nn.Conv3d(in_ch, out_ch, kernel_size=k_size, padding=pad, dilation=dil), | |
| nn.BatchNorm3d(out_ch), | |
| nn.LeakyReLU(inplace=True), | |
| nn.Conv3d(out_ch, out_ch, kernel_size=k_size, padding=pad, dilation=dil), | |
| nn.BatchNorm3d(out_ch), | |
| nn.LeakyReLU(inplace=True) | |
| ) | |
| def forward(self, x): | |
| x = self.block(x) | |
| return x | |
| class ConvBlock(nn.Module): | |
| """ (2D conv => BN => LeakyReLU) """ | |
| def __init__(self, in_ch, out_ch, k_size, pad, dil): | |
| super().__init__() | |
| self.block = nn.Sequential( | |
| nn.Conv2d(in_ch, out_ch, kernel_size=k_size, padding=pad, dilation=dil), | |
| nn.BatchNorm2d(out_ch), | |
| nn.LeakyReLU(inplace=True) | |
| ) | |
| def forward(self, x): | |
| x = self.block(x) | |
| return x | |
| # reda peak_conv zlw @20220624 | |
| # Peak Conv with fixed guard banwidth (gb) and dense reference receptive field (RRF) | |
| class PeakConv2D(nn.Module): | |
| def __init__(self, in_ch, out_ch, bias, refer_band, guard_band_dop, guard_band, signal_type): | |
| """ | |
| Args: | |
| refer_band: a single integer value or integer vector of two values. | |
| guard_band_dop: guard bandwidth for doppler input | |
| guard_band: guard banwidth for non-doppler input | |
| signal_type in ('range_doppler', 'angle_doppler', 'range_angle'). | |
| k_size, padding and stride are computed using the input fixed guard bandwidth | |
| input: x in the shape of BxCxHxW | |
| """ | |
| super(PeakConv2D, self).__init__() | |
| self.signal_type = signal_type | |
| if self.signal_type in ('range_doppler', 'angle_doppler'): | |
| # guard_band should be bi-lateral | |
| self.guard_band = (guard_band_dop, guard_band_dop) | |
| # padding order: l-r-t-b | |
| self.padding = (self.guard_band[1] + refer_band, | |
| self.guard_band[1] + refer_band, | |
| self.guard_band[0] + refer_band, | |
| self.guard_band[0] + refer_band) | |
| else: | |
| self.guard_band = (guard_band, guard_band) | |
| # padding order: l-r-t-b | |
| self.padding = (self.guard_band[1] + refer_band, | |
| self.guard_band[1] + refer_band, | |
| self.guard_band[0] + refer_band, | |
| self.guard_band[0] + refer_band) | |
| self.refer_band = refer_band | |
| self.kernel_size = (self.guard_band[0] + self.guard_band[1] + self.refer_band * 2), 4 | |
| self.stride = self.kernel_size | |
| # padding order: l-r-t-b | |
| self.zero_padding = nn.ZeroPad2d(self.padding) | |
| self.bias = bias | |
| self.pc_strd = 1 | |
| # conv block for peak receptive field (PRF) in x | |
| # take PRF as input and output the center point output of PRF; | |
| # kernel channel = in_ch*2: depth_concat(p_c, p_r) | |
| # since each p_c is expanded as a tensor of kernel scale RF, the stride = kernel_size | |
| self.peak_conv = nn.Conv2d(in_ch, out_ch, | |
| kernel_size=self.kernel_size, | |
| padding=0, | |
| stride=self.stride, | |
| bias=self.bias) | |
| #self.peak_conv.register_backward_hook(self._set_lr) | |
| # set learning rate for peak_conv | |
| def _set_lr(module, grad_input, grad_output): | |
| grad_input = (grad_input[i] * 0.1 for i in range(len(grad_input))) | |
| grad_output = (grad_output[i] * 0.1 for i in range(len(grad_output))) | |
| def forward(self, x): | |
| h, w = x.shape[2], x.shape[3] | |
| rb = self.refer_band | |
| gb = self.guard_band | |
| k_pk = self.kernel_size | |
| N = k_pk[0] * k_pk[1] | |
| dtype = x.data.type() | |
| b = x.size(0) | |
| x = self.zero_padding(x) | |
| # let p_r denote the reference point positions | |
| # p_r shape: Bx2NxHxW | |
| p_r = self._get_p_r(b, h, w, N, dtype) | |
| # sample x_r (b, c, h, w, N) using p_r | |
| x_r = self._sample_x(x, p_r, N) | |
| # sample x_c (b, c, h, w, N) using p_c | |
| p_c = self._get_p_c(b, h, w, N, dtype) | |
| x_c = self._sample_x(x, p_c, N) | |
| # getting x_prf for peak_conv | |
| # (b, c, h, w, N) ==> (b, 2c, h, w, N) | |
| # x_prf = torch.cat((x_c, x_r), 1) | |
| x_prf = x_c - x_r | |
| # (b, 2c, h*k_pk[0], w*k_pk[1]) | |
| x_prf = self._reshape_x_prf(k_pk[0], k_pk[1], x_prf) | |
| # out: (b, c, h, w) | |
| out = self.peak_conv(x_prf) | |
| return out | |
| # getting p_c (the center point coords) from the padded grid of input x | |
| def _get_p_c(self, b, h, w, N, dtype): | |
| # generating pc_grid | |
| # for UserWarning in meshgrid, indexing='ij' @20240627 | |
| p_c_x, p_c_y = torch.meshgrid( | |
| torch.arange(self.padding[2], h * self.pc_strd + self.padding[2], self.pc_strd), | |
| torch.arange(self.padding[0], w * self.pc_strd + self.padding[0], self.pc_strd), indexing='ij') | |
| p_c_x = torch.flatten(p_c_x).view(1, 1, h, w).repeat(1, N, 1, 1) | |
| p_c_y = torch.flatten(p_c_y).view(1, 1, h, w).repeat(1, N, 1, 1) | |
| # p_c: 1x2NxHxW | |
| p_c = torch.cat([p_c_x, p_c_y], 1).type(dtype) | |
| # (B, 2N, h, w) | |
| p_c = p_c.repeat(b, 1, 1, 1) | |
| return p_c | |
| # generating peak receptive field grid | |
| def _gen_prf_grid(self, rb, gb, N, dtype): | |
| h_t = -(rb + gb[0]) | |
| h_d = rb + gb[0] | |
| w_l = -(rb + gb[1]) | |
| w_r = rb + gb[1] | |
| w_prf = (rb + gb[1]) * 2 + 1 | |
| h_prf = (rb + gb[0]) * 2 + 1 | |
| prf_x_idx, prf_y_idx = torch.meshgrid( | |
| torch.arange(h_t, h_d + 1), | |
| torch.arange(w_l, w_r + 1), indexing='ij') | |
| # grid_size*2, 1 grid_size = (h_d*2+1) * (w_r*2+1) | |
| # taking positions clockwise | |
| prf_xt = prf_x_idx[0:rb] | |
| prf_xr = prf_x_idx[rb:(h_prf - 2 * rb + 1), (w_prf - rb):w_prf] | |
| prf_xd = prf_x_idx[(h_prf - rb):h_prf] | |
| prf_xl = prf_x_idx[rb:(h_prf - 2 * rb + 1), 0:rb] | |
| prf_x = torch.cat([torch.flatten(prf_xt), | |
| torch.flatten(prf_xr), | |
| torch.flatten(prf_xd), | |
| torch.flatten(prf_xl)], 0) | |
| prf_yt = prf_y_idx[0:rb] | |
| prf_yr = prf_y_idx[rb:(h_prf - 2 * rb + 1), (w_prf - rb):w_prf] | |
| prf_yd = prf_y_idx[(h_prf - rb):h_prf] | |
| prf_yl = prf_y_idx[rb:(h_prf - 2 * rb + 1), 0:rb] | |
| prf_y = torch.cat([torch.flatten(prf_yt), | |
| torch.flatten(prf_yr), | |
| torch.flatten(prf_yd), | |
| torch.flatten(prf_yl)], 0) | |
| prf = torch.cat([prf_x, prf_y], 0) | |
| prf = prf.view(1, 2 * N, 1, 1).type(dtype) | |
| return prf | |
| # getting p_r positions from each p_c | |
| def _get_p_r(self, b, h, w, N, dtype): | |
| rb = self.refer_band | |
| gb = self.guard_band | |
| # (1, 2N, 1, 1) | |
| prf = self._gen_prf_grid(rb, gb, N, dtype) | |
| # (B, 2N, h, w) | |
| p_c = self._get_p_c(b, h, w, N, dtype) | |
| # (B, 2N, h, w) | |
| p_r = p_c + prf | |
| return p_r | |
| # sampling x using p_r or p_c | |
| def _sample_x(self, x_pad, p, N): | |
| p = p.contiguous().permute(0, 2, 3, 1).floor() | |
| b, h, w, _ = p.size() | |
| h_pad = x_pad.size(2) | |
| w_pad = x_pad.size(3) | |
| c = x_pad.size(1) | |
| # (b, c, h_pad*w_pad) | |
| # strech each spatial channel of x_pad as 1-D vector | |
| x_pad = x_pad.contiguous().view(b, c, -1) | |
| # (b, h_pad, w_pad, N) | |
| # transform spatial coord of p into the 1-D index | |
| index = p[..., :N] * w_pad + p[..., N:] | |
| # index_x = torch.clamp(p[..., :N], 0, h_pad-1) | |
| # index_y = torch.clamp(p[..., N:], 0, w_pad-1) | |
| # index = index_x * w_pad + index_y | |
| # (b, c, h*w*N) | |
| index = index.contiguous().unsqueeze(dim=1).expand(-1, c, -1, -1, -1).contiguous().view(b, c, -1) | |
| index = index.long().to(x_pad.device) | |
| x_r = x_pad.gather(dim=-1, index=index).contiguous().view(b, c, h, w, N) | |
| return x_r | |
| # reshape the x_prf | |
| def _reshape_x_prf(k_h, k_w, x_prf): | |
| b, c, h, w, N = x_prf.size() | |
| x_prf = torch.cat([x_prf[..., s:s + k_w].contiguous().view(b, c, h, w * k_w) for s in range(0, N, k_w)], dim=-1) | |
| x_prf = x_prf.contiguous().view(b, c, h * k_h, w * k_w) | |
| return x_prf | |
| # double PeakConv2D block definition zlw @20220621 | |
| class DoublePeakConv2D(nn.Module): | |
| """ (2D PeakConv => BN => LeakyReLU) * 2 """ | |
| def __init__(self, in_ch, out_ch, bias, rb, gb_dop, gb, sig): | |
| super(DoublePeakConv2D, self).__init__() | |
| self.bias = bias | |
| self.refer_band = rb | |
| self.guard_band_dop = gb_dop | |
| self.guard_band = gb | |
| self.signal_type = sig | |
| self.pk_conv1 = PeakConv2D(in_ch, out_ch, bias=self.bias, | |
| refer_band=self.refer_band, | |
| guard_band_dop=self.guard_band_dop, | |
| guard_band=self.guard_band, | |
| signal_type=self.signal_type) | |
| self.bn1 = nn.BatchNorm2d(out_ch) | |
| self.act1 = nn.LeakyReLU(inplace=True) | |
| self.pk_conv2 = PeakConv2D(out_ch, out_ch, bias=self.bias, | |
| refer_band=self.refer_band, | |
| guard_band_dop=self.guard_band_dop, | |
| guard_band=self.guard_band, | |
| signal_type=self.signal_type) | |
| self.bn2 = nn.BatchNorm2d(out_ch) | |
| self.act2 = nn.LeakyReLU(inplace=True) | |
| def forward(self, x): | |
| x = self.pk_conv1(x) | |
| # print("x:", type(x)) | |
| x = self.bn1(x) | |
| x = self.act1(x) | |
| x = self.pk_conv2(x) | |
| x = self.bn2(x) | |
| x = self.act2(x) | |
| return x | |
| class ASPPBlock(nn.Module): | |
| """Atrous Spatial Pyramid Pooling | |
| Parallel conv blocks with different dilation rate | |
| """ | |
| def __init__(self, in_ch, out_ch, shape): | |
| super().__init__() | |
| self.shape = shape | |
| self.global_avg_pool = nn.AvgPool2d(self.shape) | |
| self.conv1_1x1 = nn.Conv2d(in_ch, out_ch, kernel_size=1, padding=0, dilation=1) | |
| self.single_conv_block1_1x1 = ConvBlock(in_ch, out_ch, k_size=1, pad=0, dil=1) | |
| self.single_conv_block1_3x3 = ConvBlock(in_ch, out_ch, k_size=3, pad=6, dil=6) | |
| self.single_conv_block2_3x3 = ConvBlock(in_ch, out_ch, k_size=3, pad=12, dil=12) | |
| self.single_conv_block3_3x3 = ConvBlock(in_ch, out_ch, k_size=3, pad=18, dil=18) | |
| def forward(self, x): | |
| x1 = F.interpolate(self.global_avg_pool(x), size=self.shape, align_corners=False, | |
| mode='bilinear') | |
| x1 = self.conv1_1x1(x1) | |
| x2 = self.single_conv_block1_1x1(x) | |
| x3 = self.single_conv_block1_3x3(x) | |
| x4 = self.single_conv_block2_3x3(x) | |
| x5 = self.single_conv_block3_3x3(x) | |
| x_cat = torch.cat((x2, x3, x4, x5, x1), 1) | |
| return x_cat | |
| class EncodingBranch(nn.Module): | |
| """ | |
| Encoding branch for a single radar view | |
| PARAMETERS | |
| ---------- | |
| signal_type: str | |
| Type of radar view. | |
| Supported: 'range_doppler', 'range_angle' and 'angle_doppler' | |
| """ | |
| def __init__(self, signal_type, n_frames, in_ch=1): | |
| super().__init__() | |
| self.signal_type = signal_type | |
| self.n_frames = n_frames | |
| self.in_ch = in_ch | |
| self.double_3dconv_block1 = Double3DConvBlock(in_ch=self.in_ch, out_ch=32, | |
| k_size=(1+n_frames//2, 3, 3), | |
| pad=(0, 1, 1), dil=1) | |
| self.doppler_max_pool = nn.MaxPool2d(2, stride=(1, 2)) | |
| self.max_pool = nn.MaxPool2d(2, stride=2) | |
| # self.double_conv_block2 = DoubleConvBlock(in_ch=128, out_ch=128, k_size=3, | |
| # pad=1, dil=1) | |
| self.double_conv_block2 = DoubleConvBlock(in_ch=32, out_ch=64, k_size=3, | |
| pad=1, dil=1) | |
| # self.double_pkc_block = DoublePeakConv2D(in_ch=32, out_ch=64, | |
| # bias=True, | |
| # rb=1, | |
| # gb_dop=1, | |
| # gb=1, | |
| # sig=self.signal_type) | |
| self.single_conv_block1_1x1 = ConvBlock(in_ch=64, out_ch=128, k_size=1, | |
| pad=0, dil=1) | |
| def forward(self, x): | |
| x1 = self.double_3dconv_block1(x) | |
| x1 = torch.squeeze(x1, 2) | |
| x1_pad = F.pad(x1, (0, 0, 0, 0), "constant", 0) | |
| x1_down = self.max_pool(x1_pad) | |
| x2 = self.double_conv_block2(x1_down) | |
| x2_pad = F.pad(x2, (0, 0, 0, 1), "constant", 0) | |
| x2_down = self.doppler_max_pool(x2_pad) # | |
| x3 = self.single_conv_block1_1x1(x2_down) | |
| # return input of ASPP block + latent features | |
| return x2_down, x3 | |
| class TMVANet_SV(nn.Module): | |
| """ | |
| Temporal Singel-View with ASPP Network (TMVA-Net) | |
| PARAMETERS | |
| ---------- | |
| n_classes: int | |
| Number of classes used for the semantic segmentation task | |
| n_frames: int | |
| Total numer of frames used as a sequence | |
| """ | |
| def __init__(self, n_classes, n_frames, in_ch, w, h): | |
| super().__init__() | |
| self.n_classes = n_classes | |
| self.n_frames = n_frames | |
| self.in_ch = in_ch | |
| self.rd_shape = (int(w//2), int(h//4)) | |
| # Backbone (encoding) | |
| self.rd_encoding_branch = EncodingBranch('range_doppler', self.n_frames, self.in_ch) | |
| # ASPP Blocks | |
| self.rd_aspp_block = ASPPBlock(in_ch=64, out_ch=128, shape=self.rd_shape) | |
| self.rd_single_conv_block1_1x1 = ConvBlock(in_ch=640, out_ch=128, k_size=1, pad=0, dil=1) | |
| # Decoding | |
| # self.rd_single_conv_block2_1x1 = ConvBlock(in_ch=128, out_ch=128, k_size=1, pad=0, dil=1) | |
| # Pallel range-Doppler (RD) and range-angle (RA) decoding branches | |
| self.rd_upconv1 = nn.ConvTranspose2d(256, 128, (2, 2), stride=(2, 2)) | |
| self.rd_double_conv_block1 = DoubleConvBlock(in_ch=128, out_ch=128, k_size=3, | |
| pad=1, dil=1) | |
| self.rd_upconv2 = nn.ConvTranspose2d(128, 128, (1, 2), stride=(1, 2)) | |
| self.rd_double_conv_block2 = DoubleConvBlock(in_ch=128, out_ch=128, k_size=3, | |
| pad=1, dil=1) | |
| # Final 1D convs | |
| self.rd_final = nn.Conv2d(in_channels=128, out_channels=n_classes, kernel_size=1) | |
| def forward(self, x_rd): | |
| # Backbone | |
| rd_features, rd_latent = self.rd_encoding_branch(x_rd) | |
| # ASPP blocks | |
| x2_rd = self.rd_aspp_block(rd_features) | |
| x2_rd = self.rd_single_conv_block1_1x1(x2_rd) | |
| # Latent Space + ASPP features | |
| x4_rd = torch.cat((x2_rd, rd_latent), 1) | |
| # Parallel decoding branches with upconvs | |
| x4_rd = self.rd_upconv1(x4_rd) | |
| x4_rd = self.rd_double_conv_block1(x4_rd) | |
| x4_rd = self.rd_upconv2(x4_rd) | |
| x4_rd = self.rd_double_conv_block2(x4_rd) | |
| # Final 1D convolutions | |
| x4_rd = self.rd_final(x4_rd) | |
| return x4_rd | |
| if __name__ == '__main__': | |
| data = torch.randn(8,1,1,128,1024) | |
| model = TMVANet_SV(2,1,1,128,1024) | |
| pre = model(data) | |
| print(pre) |