Spaces:
Running on Zero
Running on Zero
| import torch,pdb,os,sys | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import numpy as np | |
| code_dir = os.path.dirname(os.path.realpath(__file__)) | |
| sys.path.append(f'{code_dir}/../') | |
| from Utils import AMP_DTYPE | |
| import Utils as U | |
| try: | |
| import triton | |
| import triton.language as tl | |
| except Exception: | |
| triton = None | |
| tl = None | |
| def _is_contiguous(tensor: torch.Tensor) -> bool: | |
| if torch.jit.is_scripting(): | |
| return tensor.is_contiguous() | |
| else: | |
| return tensor.is_contiguous(memory_format=torch.contiguous_format) | |
| class LayerNorm2d(nn.LayerNorm): | |
| r""" https://huggingface.co/spaces/Roll20/pet_score/blob/b258ef28152ab0d5b377d9142a23346f863c1526/lib/timm/models/convnext.py#L85 | |
| LayerNorm for channels_first tensors with 2d spatial dimensions (ie N, C, H, W). | |
| """ | |
| def __init__(self, normalized_shape, eps=1e-6): | |
| """ | |
| @normalized_shape: channel dim | |
| """ | |
| super().__init__(normalized_shape, eps=eps) | |
| def forward(self, x) -> torch.Tensor: | |
| """ | |
| @x: (B,C,H,W) | |
| """ | |
| if _is_contiguous(x): | |
| return F.layer_norm(x.permute(0, 2, 3, 1), self.normalized_shape, self.weight, self.bias, self.eps).permute(0, 3, 1, 2).contiguous() | |
| else: | |
| s, u = torch.var_mean(x, dim=1, keepdim=True) | |
| x = (x - u) * torch.rsqrt(s + self.eps) | |
| x = x * self.weight[:, None, None] + self.bias[:, None, None] | |
| return x | |
| class BasicConv(nn.Module): | |
| def __init__(self, in_channels, out_channels, deconv=False, is_3d=False, bn=True, relu=True, norm='batch', **kwargs): | |
| super(BasicConv, self).__init__() | |
| self.relu = nn.LeakyReLU(inplace=True) if relu else nn.Identity() | |
| self.use_bn = bn | |
| self.bn = nn.Identity() | |
| if is_3d: | |
| if deconv: | |
| self.conv = nn.ConvTranspose3d(in_channels, out_channels, bias=False, **kwargs) | |
| else: | |
| self.conv = nn.Conv3d(in_channels, out_channels, bias=False, **kwargs) | |
| if self.use_bn: | |
| if norm=='batch': | |
| self.bn = nn.BatchNorm3d(out_channels) | |
| elif norm=='instance': | |
| self.bn = nn.InstanceNorm3d(out_channels) | |
| else: | |
| if deconv: | |
| self.conv = nn.ConvTranspose2d(in_channels, out_channels, bias=False, **kwargs) | |
| else: | |
| self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs) | |
| if self.use_bn: | |
| if norm=='batch': | |
| self.bn = nn.BatchNorm2d(out_channels) | |
| elif norm=='instance': | |
| self.bn = nn.InstanceNorm2d(out_channels) | |
| def forward(self, x): | |
| x = self.conv(x) | |
| if self.use_bn: | |
| x = self.bn(x) | |
| if isinstance(self.relu, bool): | |
| if self.relu: | |
| self.relu = nn.LeakyReLU(inplace=True) | |
| else: | |
| self.relu = nn.Identity() | |
| x = self.relu(x) | |
| return x | |
| class Conv3dNormActReduced(nn.Module): | |
| def __init__(self, C_in, C_out, hidden=None, kernel_size=3, kernel_disp=None, stride=1, norm=nn.BatchNorm3d): | |
| super().__init__() | |
| if kernel_disp is None: | |
| kernel_disp = kernel_size | |
| if hidden is None: | |
| hidden = C_out | |
| self.conv1 = nn.Sequential( | |
| nn.Conv3d(C_in, hidden, kernel_size=(1,kernel_size,kernel_size), padding=(0, kernel_size//2, kernel_size//2), stride=(1, stride, stride)), | |
| norm(hidden), | |
| nn.ReLU(), | |
| ) | |
| self.conv2 = nn.Sequential( | |
| nn.Conv3d(hidden, C_out, kernel_size=(kernel_disp, 1, 1), padding=(kernel_disp//2, 0, 0), stride=(stride, 1, 1)), | |
| norm(C_out), | |
| nn.ReLU(), | |
| ) | |
| def forward(self, x): | |
| """ | |
| @x: (B,C,D,H,W) | |
| """ | |
| x = self.conv1(x) | |
| x = self.conv2(x) | |
| return x | |
| class ResnetBasicBlock(nn.Module): | |
| def __init__(self, inplanes, planes, kernel_size=3, stride=1, padding=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=nn.BatchNorm2d, bias=False): | |
| super().__init__() | |
| self.norm_layer = norm_layer | |
| if groups != 1 or base_width != 64: | |
| raise ValueError('BasicBlock only supports groups=1 and base_width=64') | |
| if dilation > 1: | |
| raise NotImplementedError("Dilation > 1 not supported in BasicBlock") | |
| # Both self.conv1 and self.downsample layers downsample the input when stride != 1 | |
| self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=kernel_size, stride=stride, bias=bias, padding=padding) | |
| if self.norm_layer is not None: | |
| self.bn1 = norm_layer(planes) | |
| self.relu = nn.ReLU(inplace=True) | |
| self.conv2 = nn.Conv2d(planes, planes, kernel_size=kernel_size, bias=bias, padding=padding) | |
| if self.norm_layer is not None: | |
| self.bn2 = norm_layer(planes) | |
| self.downsample = downsample | |
| self.stride = stride | |
| def forward(self, x): | |
| identity = x | |
| out = self.conv1(x) | |
| if self.norm_layer is not None: | |
| out = self.bn1(out) | |
| out = self.relu(out) | |
| out = self.conv2(out) | |
| if self.norm_layer is not None: | |
| out = self.bn2(out) | |
| if self.downsample is not None: | |
| identity = self.downsample(x) | |
| out += identity | |
| out = self.relu(out) | |
| return out | |
| class ResnetBasicBlock3D(nn.Module): | |
| def __init__(self, inplanes, planes, kernel_size=3, stride=1, padding=1, downsample=None, groups=1, base_width=64, dilation=1, norm_layer=nn.BatchNorm3d, bias=False): | |
| super().__init__() | |
| self.norm_layer = norm_layer | |
| if groups != 1 or base_width != 64: | |
| raise ValueError('BasicBlock only supports groups=1 and base_width=64') | |
| if dilation > 1: | |
| raise NotImplementedError("Dilation > 1 not supported in BasicBlock") | |
| # Both self.conv1 and self.downsample layers downsample the input when stride != 1 | |
| self.conv1 = nn.Conv3d(inplanes, planes, kernel_size=kernel_size, stride=stride, bias=bias, padding=padding) | |
| if self.norm_layer is not None: | |
| self.bn1 = norm_layer(planes) | |
| self.relu = nn.ReLU(inplace=True) | |
| self.conv2 = nn.Conv3d(planes, planes, kernel_size=kernel_size, bias=bias, padding=padding) | |
| if self.norm_layer is not None: | |
| self.bn2 = norm_layer(planes) | |
| self.downsample = downsample | |
| self.stride = stride | |
| def forward(self, x): | |
| identity = x | |
| out = self.conv1(x) | |
| if self.norm_layer is not None: | |
| out = self.bn1(out) | |
| out = self.relu(out) | |
| out = self.conv2(out) | |
| if self.norm_layer is not None: | |
| out = self.bn2(out) | |
| if self.downsample is not None: | |
| identity = self.downsample(x) | |
| out += identity | |
| out = self.relu(out) | |
| return out | |
| class FlashMultiheadAttention(nn.Module): | |
| def __init__(self, embed_dim, num_heads): | |
| super().__init__() | |
| self.num_heads = num_heads | |
| self.embed_dim = embed_dim | |
| self.head_dim = embed_dim // num_heads | |
| assert self.head_dim * num_heads == self.embed_dim, "embed_dim must be divisible by num_heads" | |
| self.q_proj = nn.Linear(embed_dim, embed_dim) | |
| self.k_proj = nn.Linear(embed_dim, embed_dim) | |
| self.v_proj = nn.Linear(embed_dim, embed_dim) | |
| self.out_proj = nn.Linear(embed_dim, embed_dim) | |
| def forward(self, query, key, value, attn_mask=None, window_size=(-1,-1)): | |
| """ | |
| @query: (B,L,C) | |
| """ | |
| B,L,C = query.shape | |
| Q = self.q_proj(query) | |
| K = self.k_proj(key) | |
| V = self.v_proj(value) | |
| Q = Q.view(Q.size(0), Q.size(1), self.num_heads, self.head_dim) | |
| K = K.view(K.size(0), K.size(1), self.num_heads, self.head_dim) | |
| V = V.view(V.size(0), V.size(1), self.num_heads, self.head_dim) | |
| attn_output = F.scaled_dot_product_attention(Q, K, V) | |
| attn_output = attn_output.reshape(B,L,-1) | |
| output = self.out_proj(attn_output) | |
| return output | |
| class FlashAttentionTransformerEncoderLayer(nn.Module): | |
| def __init__(self, embed_dim, num_heads, dim_feedforward, dropout=0.1, act=nn.GELU, norm=nn.LayerNorm): | |
| super().__init__() | |
| self.self_attn = FlashMultiheadAttention(embed_dim, num_heads) | |
| self.act = act() | |
| self.linear1 = nn.Linear(embed_dim, dim_feedforward) | |
| self.dropout = nn.Dropout(dropout) | |
| self.linear2 = nn.Linear(dim_feedforward, embed_dim) | |
| self.norm1 = norm(embed_dim) | |
| self.norm2 = norm(embed_dim) | |
| self.dropout1 = nn.Dropout(dropout) | |
| self.dropout2 = nn.Dropout(dropout) | |
| def forward(self, src, src_mask=None, window_size=(-1, -1)): | |
| dtype = src.dtype | |
| src2 = self.self_attn(src, src, src, src_mask, window_size=window_size) | |
| src = src + self.dropout1(src2) | |
| src = self.norm1(src).to(dtype) | |
| src2 = self.linear2(self.dropout(self.act(self.linear1(src)))) | |
| src = src + self.dropout2(src2) | |
| src = self.norm2(src).to(dtype) | |
| return src | |
| class Conv2x(nn.Module): | |
| def __init__(self, in_channels, out_channels, deconv=False, is_3d=False, concat=True, keep_concat=True, bn=True, relu=True, keep_dispc=False): | |
| super(Conv2x, self).__init__() | |
| self.concat = concat | |
| self.is_3d = is_3d | |
| if deconv and is_3d: | |
| kernel = (4, 4, 4) | |
| elif deconv: | |
| kernel = 4 | |
| else: | |
| kernel = 3 | |
| if deconv and is_3d and keep_dispc: | |
| kernel = (1, 4, 4) | |
| stride = (1, 2, 2) | |
| padding = (0, 1, 1) | |
| self.conv1 = BasicConv(in_channels, out_channels, deconv, is_3d, bn=bn, relu=True, kernel_size=kernel, stride=stride, padding=padding) | |
| else: | |
| self.conv1 = BasicConv(in_channels, out_channels, deconv, is_3d, bn=bn, relu=True, kernel_size=kernel, stride=2, padding=1) | |
| if self.concat: | |
| mul = 2 if keep_concat else 1 | |
| self.conv2 = BasicConv(out_channels*2, out_channels*mul, False, is_3d, bn, relu, kernel_size=3, stride=1, padding=1) | |
| else: | |
| self.conv2 = BasicConv(out_channels, out_channels, False, is_3d, bn, relu, kernel_size=3, stride=1, padding=1) | |
| def forward(self, x, rem): | |
| x = self.conv1(x) | |
| if x.shape != rem.shape: | |
| x = F.interpolate(x, size=(rem.shape[-2], rem.shape[-1]), mode='bilinear') | |
| if self.concat: | |
| x = torch.cat((x, rem), 1) | |
| else: | |
| x = x + rem | |
| x = self.conv2(x) | |
| return x | |
| class BasicConv_IN(nn.Module): | |
| def __init__(self, in_channels, out_channels, deconv=False, is_3d=False, IN=True, relu=True, **kwargs): | |
| super(BasicConv_IN, self).__init__() | |
| if relu: | |
| self.relu = nn.LeakyReLU(inplace=True) | |
| else: | |
| self.relu = nn.Identity() | |
| self.use_in = IN | |
| if is_3d: | |
| if deconv: | |
| self.conv = nn.ConvTranspose3d(in_channels, out_channels, bias=False, **kwargs) | |
| else: | |
| self.conv = nn.Conv3d(in_channels, out_channels, bias=False, **kwargs) | |
| self.IN = nn.InstanceNorm3d(out_channels) | |
| else: | |
| if deconv: | |
| self.conv = nn.ConvTranspose2d(in_channels, out_channels, bias=False, **kwargs) | |
| else: | |
| self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs) | |
| self.IN = nn.InstanceNorm2d(out_channels) | |
| def forward(self, x): | |
| x = self.conv(x) | |
| if self.use_in: | |
| x = self.IN(x) | |
| if isinstance(self.relu, bool): | |
| if self.relu: | |
| self.relu = nn.LeakyReLU(inplace=True) | |
| else: | |
| self.relu = nn.Identity() | |
| x = self.relu(x) | |
| return x | |
| class Conv2x_IN(nn.Module): | |
| def __init__(self, in_channels, out_channels, c_middle=None, deconv=False, is_3d=False, concat=True, keep_concat=True, IN=True, relu=True, keep_dispc=False): | |
| super(Conv2x_IN, self).__init__() | |
| self.concat = concat | |
| self.is_3d = is_3d | |
| if deconv and is_3d: | |
| kernel = (4, 4, 4) | |
| elif deconv: | |
| kernel = 4 | |
| else: | |
| kernel = 3 | |
| if c_middle is None: | |
| c_middle = out_channels | |
| if deconv and is_3d and keep_dispc: | |
| kernel = (1, 4, 4) | |
| stride = (1, 2, 2) | |
| padding = (0, 1, 1) | |
| self.conv1 = BasicConv_IN(in_channels, c_middle, deconv, is_3d, IN=True, relu=True, kernel_size=kernel, stride=stride, padding=padding) | |
| else: | |
| self.conv1 = BasicConv_IN(in_channels, c_middle, deconv, is_3d, IN=True, relu=True, kernel_size=kernel, stride=2, padding=1) | |
| if self.concat: | |
| mul = 2 if keep_concat else 1 | |
| self.conv2 = ResnetBasicBlock(out_channels*2, out_channels*mul, kernel_size=3, stride=1, padding=1, norm_layer=nn.InstanceNorm2d) | |
| else: | |
| self.conv2 = BasicConv_IN(c_middle, out_channels, False, is_3d, IN, relu, kernel_size=3, stride=1, padding=1) | |
| def forward(self, x, rem): | |
| x = self.conv1(x) | |
| if x.shape != rem.shape: | |
| x = F.interpolate(x, size=(rem.shape[-2], rem.shape[-1]), mode='bilinear') | |
| if self.concat: | |
| x = torch.cat((x, rem), 1) | |
| else: | |
| x = x + rem | |
| x = self.conv2(x) | |
| return x | |
| def build_gwc_volume_optimized_pytorch1(refimg_fea: torch.Tensor, targetimg_fea: torch.Tensor, maxdisp: int, num_groups: int, normalize=True): | |
| dtype = refimg_fea.dtype | |
| B, C, H, W = refimg_fea.shape | |
| channels_per_group = C // num_groups | |
| ref_volume = refimg_fea.unsqueeze(2).expand(B, C, maxdisp, H, W) | |
| padded_target = F.pad(targetimg_fea, (maxdisp - 1, 0, 0, 0)) | |
| unfolded_target = padded_target.unfold(3, W, 1) | |
| target_volume = torch.flip(unfolded_target, [3]).permute(0, 1, 3, 2, 4) | |
| ref_volume = ref_volume.view(B, num_groups, channels_per_group, maxdisp, H, W) | |
| target_volume = target_volume.view(B, num_groups, channels_per_group, maxdisp, H, W) | |
| if normalize: | |
| ref_volume = F.normalize(ref_volume.float(), dim=2).to(dtype) | |
| target_volume = F.normalize(target_volume.float(), dim=2).to(dtype) | |
| cost_volume = (ref_volume * target_volume).sum(dim=2) | |
| return cost_volume.contiguous() | |
| if triton is not None and torch.cuda.is_available(): | |
| def _gwc_triton_kernel(ref_ptr, tar_ptr, ref_norm_ptr, tar_norm_ptr, out_ptr, BH, C, W, D: tl.constexpr, G: tl.constexpr, K: tl.constexpr, | |
| stride_rn, stride_rw, stride_rc, stride_tn, stride_tw, stride_tc, | |
| stride_nn, stride_ng, stride_nw, | |
| stride_on, stride_og, stride_od, stride_ow, | |
| NORMALIZE: tl.constexpr, | |
| BLOCK_C: tl.constexpr, BLOCK_W: tl.constexpr, BLOCK_D: tl.constexpr): | |
| pid0 = tl.program_id(0) | |
| db = tl.program_id(1) | |
| wb = tl.program_id(2) | |
| bh = pid0 // G | |
| g = pid0 % G | |
| w_off = wb*BLOCK_W + tl.arange(0, BLOCK_W) | |
| d_off = db*BLOCK_D + tl.arange(0, BLOCK_D) | |
| w_mask = w_off < W | |
| w_src = w_off[None, :] - d_off[:, None] | |
| td_mask = (w_src >= 0) & w_mask[None, :] | |
| acc = tl.zeros((BLOCK_D, BLOCK_W), dtype=tl.float32) | |
| for k0 in tl.static_range(0, K, BLOCK_C): | |
| k_off = k0 + tl.arange(0, BLOCK_C) | |
| k_mask = k_off < K | |
| c_idx = g*K + k_off | |
| ref_ptrs = ref_ptr + bh*stride_rn + w_off[None, :]*stride_rw + c_idx[:, None]*stride_rc | |
| ref_vals = tl.load(ref_ptrs, mask=k_mask[:, None] & w_mask[None, :], other=0.).to(tl.float32) | |
| tar_ptrs = tar_ptr + bh*stride_tn + w_src[None, :, :]*stride_tw + c_idx[:, None, None]*stride_tc | |
| tar_vals = tl.load(tar_ptrs, mask=k_mask[:, None, None] & td_mask[None, :, :], other=0.).to(tl.float32) | |
| acc += tl.sum(tar_vals * ref_vals[:, None, :], axis=0) | |
| if NORMALIZE: | |
| norm_offset = bh*stride_nn + g*stride_ng | |
| ref_norm = tl.load(ref_norm_ptr + norm_offset + w_off*stride_nw, mask=w_mask, other=1.0).to(tl.float32) | |
| tar_norm = tl.load(tar_norm_ptr + norm_offset + w_src*stride_nw, mask=td_mask, other=1.0).to(tl.float32) | |
| denom = (ref_norm[None, :] * tar_norm) + 1e-5 | |
| acc = acc / denom | |
| out_ptrs = out_ptr + bh*stride_on + g*stride_og + d_off[:, None]*stride_od + w_off[None, :]*stride_ow | |
| tl.store(out_ptrs, acc, mask=w_mask[None, :]) | |
| def build_gwc_volume_triton(refimg_fea: torch.Tensor, targetimg_fea: torch.Tensor, maxdisp: int, num_groups: int, normalize=True): | |
| if triton is None: | |
| raise RuntimeError('Triton is not available. Please install triton to use build_gwc_volume_triton.') | |
| B, C, H, W = refimg_fea.shape | |
| assert maxdisp > 0 and C % num_groups == 0 | |
| K = C // num_groups | |
| in_dtype = refimg_fea.dtype if refimg_fea.dtype in (torch.float16, torch.bfloat16, torch.float32) else torch.float32 | |
| if normalize: | |
| ref_norm = refimg_fea.float().view(B, num_groups, K, H, W).norm(dim=2) | |
| tar_norm = targetimg_fea.float().view(B, num_groups, K, H, W).norm(dim=2) | |
| ref_norm = ref_norm.permute(0, 2, 1, 3).reshape(B*H, num_groups, W).to(in_dtype).contiguous() | |
| tar_norm = tar_norm.permute(0, 2, 1, 3).reshape(B*H, num_groups, W).to(in_dtype).contiguous() | |
| else: | |
| # Dummy tensors; kernel won't read them when NORMALIZE=False | |
| ref_norm = refimg_fea.new_empty((1, 1, 1), dtype=in_dtype) | |
| tar_norm = refimg_fea.new_empty((1, 1, 1), dtype=in_dtype) | |
| ref = refimg_fea.to(in_dtype) | |
| tar = targetimg_fea.to(in_dtype) | |
| ref_bhwc = ref.permute(0, 2, 3, 1).view(B * H, W, C).contiguous() | |
| tar_bhwc = tar.permute(0, 2, 3, 1).view(B * H, W, C).contiguous() | |
| out_bhw = torch.empty((B * H, num_groups, maxdisp, W), device=ref.device, dtype=in_dtype) | |
| BH = B * H | |
| D_eff = min(maxdisp, W) | |
| grid = lambda META: (BH * num_groups, triton.cdiv(D_eff, META['BLOCK_D']), triton.cdiv(W, META['BLOCK_W'])) | |
| _gwc_triton_kernel[grid](ref_bhwc, tar_bhwc, ref_norm, tar_norm, out_bhw, BH, C, W, D_eff, num_groups, K, | |
| ref_bhwc.stride(0), ref_bhwc.stride(1), ref_bhwc.stride(2), | |
| tar_bhwc.stride(0), tar_bhwc.stride(1), tar_bhwc.stride(2), | |
| ref_norm.stride(0), ref_norm.stride(1), ref_norm.stride(2), | |
| out_bhw.stride(0), out_bhw.stride(1), out_bhw.stride(2), out_bhw.stride(3), | |
| NORMALIZE=normalize) | |
| if D_eff < maxdisp: out_bhw[:, :, D_eff:, :] = 0 | |
| volume = out_bhw.view(B, H, num_groups, maxdisp, W).permute(0, 2, 3, 1, 4).contiguous() | |
| return volume | |
| def build_concat_volume_optimized_pytorch(refimg_fea, targetimg_fea, maxdisp:int): | |
| B, C, H, W = refimg_fea.shape | |
| ref_volume = refimg_fea.unsqueeze(2).expand(B, C, maxdisp, H, W) | |
| shifted_target_list = [F.pad(targetimg_fea, (int(d), 0, 0, 0), "constant", 0.0)[:, :, :, :W] for d in range(maxdisp)] | |
| target_volume = torch.stack(shifted_target_list, dim=2) | |
| volume = torch.cat((ref_volume, target_volume), dim=1) | |
| return volume.contiguous() | |
| def build_concat_volume_optimized_pytorch1(refimg_fea, targetimg_fea, maxdisp:int): | |
| B, C, H, W = refimg_fea.shape | |
| ref_volume = refimg_fea.unsqueeze(2).expand(B, C, maxdisp, H, W) | |
| padded_target = F.pad(targetimg_fea, (maxdisp - 1, 0, 0, 0)) # (B, C, H, W + maxdisp - 1) | |
| unfolded_target = padded_target.unfold(dimension=3, size=W, step=1) # (B, C, H, maxdisp, W) | |
| target_volume = torch.flip(unfolded_target, [3]).permute(0, 1, 3, 2, 4) | |
| volume = torch.cat((ref_volume, target_volume), dim=1) | |
| return volume.contiguous() | |
| def disparity_regression(x, maxdisp): | |
| assert len(x.shape) == 4 | |
| disp_values = torch.arange(0, maxdisp, dtype=x.dtype, device=x.device) | |
| disp_values = disp_values.reshape(1, maxdisp, 1, 1) | |
| return torch.sum(x * disp_values, 1, keepdim=True) #(B,1,H,W) | |
| class FeatureAtt(nn.Module): | |
| def __init__(self, cv_chan, feat_chan): | |
| super(FeatureAtt, self).__init__() | |
| self.feat_att = nn.Sequential( | |
| BasicConv(feat_chan, feat_chan//2, kernel_size=1, stride=1, padding=0), | |
| nn.Conv2d(feat_chan//2, cv_chan, 1) | |
| ) | |
| def forward(self, cv, feat): | |
| ''' | |
| @cv: cost volume (B,C,D,H,W) | |
| @feat: (B,C,H,W) | |
| ''' | |
| feat_att = self.feat_att(feat).unsqueeze(2) #(B,C,1,H,W) | |
| cv = torch.sigmoid(feat_att)*cv | |
| return cv | |
| def context_upsample(disp_low, up_weights): | |
| """ | |
| @disp_low: (b,1,h,w) 1/4 resolution | |
| @up_weights: (b,9,4*h,4*w) Image resolution | |
| """ | |
| b, c, h, w = disp_low.shape | |
| disp_unfold = F.unfold(disp_low.reshape(b,c,h,w),3,1,1).reshape(b,-1,h,w) | |
| disp_unfold = F.interpolate(disp_unfold,(h*4,w*4),mode='nearest').reshape(b,9,h*4,w*4) | |
| disp = (disp_unfold*up_weights).sum(1) | |
| return disp | |
| class PositionalEmbedding(nn.Module): | |
| def __init__(self, d_model, max_len=512): | |
| super().__init__() | |
| # Compute the positional encodings once in log space. | |
| pe = torch.zeros(max_len, d_model, dtype=torch.float) | |
| pe.require_grad = False | |
| position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1) #(N,1) | |
| div_term = (torch.arange(0, d_model, 2, dtype=torch.float) * -(np.log(10000.0) / d_model)).exp()[None] | |
| pe[:, 0::2] = torch.sin(position * div_term) #(N, d_model/2) | |
| pe[:, 1::2] = torch.cos(position * div_term) | |
| pe = pe.unsqueeze(0) | |
| self.pe = pe | |
| def forward(self, x, resize_embed=False): | |
| ''' | |
| @x: (B,N,D) | |
| ''' | |
| dtype = x.dtype | |
| self.pe = self.pe.to(x.device).to(x.dtype) | |
| pe = self.pe | |
| if pe.shape[1]<x.shape[1]: | |
| if resize_embed: | |
| pe = F.interpolate(pe.permute(0,2,1), size=x.shape[1], mode='linear', align_corners=True).permute(0,2,1) | |
| else: | |
| raise RuntimeError(f'x:{x.shape}, pe:{pe.shape}') | |
| return (x + pe[:, :x.size(1)]).to(dtype) | |
| class CostVolumeDisparityAttention(nn.Module): | |
| def __init__(self, d_model, nhead, dim_feedforward, dropout=0.1, act=nn.GELU, norm_first=False, num_transformer=6, max_len=512, resize_embed=False): | |
| super().__init__() | |
| self.resize_embed = resize_embed | |
| self.sa = nn.ModuleList([]) | |
| for _ in range(num_transformer): | |
| self.sa.append(FlashAttentionTransformerEncoderLayer(embed_dim=d_model, num_heads=nhead, dim_feedforward=dim_feedforward, act=act, dropout=dropout)) | |
| self.pos_embed0 = PositionalEmbedding(d_model, max_len=max_len) | |
| def forward(self, cv, window_size=(-1,-1)): | |
| """ | |
| @cv: (B,C,D,H,W) where D is max disparity | |
| """ | |
| x = cv | |
| B,C,D,H,W = x.shape | |
| x = x.permute(0,3,4,2,1).reshape(B*H*W, D, C) | |
| x = self.pos_embed0(x, resize_embed=self.resize_embed) #!NOTE No resize since disparity is pre-determined | |
| for i in range(len(self.sa)): | |
| x = self.sa[i](x, window_size=window_size) | |
| x = x.reshape(B,H,W,D,C).permute(0,4,3,1,2) | |
| return x | |
| class ChannelAttentionEnhancement(nn.Module): | |
| def __init__(self, in_planes, ratio=16): | |
| """From selective-IGEV | |
| """ | |
| super(ChannelAttentionEnhancement, self).__init__() | |
| self.avg_pool = nn.AdaptiveAvgPool2d(1) | |
| self.max_pool = nn.AdaptiveMaxPool2d(1) | |
| self.fc = nn.Sequential(nn.Conv2d(in_planes, in_planes // 16, 1, bias=False), | |
| nn.ReLU(), | |
| nn.Conv2d(in_planes // 16, in_planes, 1, bias=False)) | |
| self.sigmoid = nn.Sigmoid() | |
| def forward(self, x): | |
| avg_out = self.fc(self.avg_pool(x)) | |
| max_out = self.fc(self.max_pool(x)) | |
| out = avg_out + max_out | |
| return self.sigmoid(out) | |
| class SpatialAttentionExtractor(nn.Module): | |
| def __init__(self, kernel_size=7): | |
| """From selective-IGEV | |
| """ | |
| super(SpatialAttentionExtractor, self).__init__() | |
| self.samconv = nn.Conv2d(2, 1, kernel_size, padding=kernel_size//2, bias=False) | |
| self.sigmoid = nn.Sigmoid() | |
| def forward(self, x): | |
| avg_out = torch.mean(x, dim=1, keepdim=True) | |
| max_out, _ = torch.max(x, dim=1, keepdim=True) | |
| x = torch.cat([avg_out, max_out], dim=1) | |
| x = self.samconv(x) | |
| return self.sigmoid(x) | |
| class EdgeNextConvEncoder(nn.Module): | |
| def __init__(self, dim, layer_scale_init_value=1e-6, expan_ratio=4, kernel_size=7, norm='layer'): | |
| """https://github.com/mmaaz60/EdgeNeXt/blob/main/models/conv_encoder.py#L7 | |
| """ | |
| super().__init__() | |
| self.dwconv = nn.Conv2d(dim, dim, kernel_size=kernel_size, padding=kernel_size // 2, groups=dim) | |
| if norm=='layer': | |
| self.norm = LayerNorm2d(dim, eps=1e-6) | |
| elif norm=='batch': | |
| self.norm = nn.BatchNorm2d(dim) | |
| else: | |
| self.norm = nn.Identity() | |
| self.pwconv1 = nn.Linear(dim, expan_ratio * dim) | |
| self.act = nn.GELU() | |
| self.pwconv2 = nn.Linear(expan_ratio * dim, dim) | |
| self.gamma = nn.Parameter(layer_scale_init_value * torch.ones(dim), requires_grad=True) if layer_scale_init_value > 0 else None | |
| def forward(self, x): | |
| input = x | |
| x = self.dwconv(x) | |
| x = self.norm(x) | |
| x = x.permute(0, 2, 3, 1) # (N, C, H, W) -> (N, H, W, C) | |
| x = self.pwconv1(x) | |
| x = self.act(x) | |
| x = self.pwconv2(x) | |
| if self.gamma is not None: | |
| x = self.gamma * x | |
| x = x.permute(0, 3, 1, 2) # (N, H, W, C) -> (N, C, H, W) | |
| x = input + x | |
| return x | |