import math import torch import torch.nn.functional as F from einops import rearrange, repeat from timm.models.layers import trunc_normal_ from torch import nn from ..util import make_coordinate_grid MIN_NUM_PATCHES = 16 BN_MOMENTUM = 0.1 class Residual(nn.Module): def __init__(self, fn, num_keypoints=10): super().__init__() self.fn = fn self.num_keypoints = num_keypoints def forward(self, x, **kwargs): return self.fn(x, **kwargs) + x class PreNorm(nn.Module): def __init__(self, dim, fn, fusion_factor=1): super().__init__() self.norm = nn.LayerNorm(dim * fusion_factor) self.fn = fn def forward(self, x, **kwargs): return self.fn(self.norm(x), **kwargs) class QKVPreNorm(nn.Module): def __init__(self, dim, fn, fusion_factor=1): super().__init__() self.norm = nn.LayerNorm(dim * fusion_factor) self.fn = fn def forward(self, q, k, v, **kwargs): return self.fn(self.norm(q), self.norm(k), self.norm(v), **kwargs) class FeedForward(nn.Module): def __init__(self, dim, hidden_dim, dropout=0.0): super().__init__() self.net = nn.Sequential( nn.Linear(dim, hidden_dim), nn.GELU(), nn.Dropout(dropout), nn.Linear(hidden_dim, dim), nn.Dropout(dropout), ) def forward(self, x): return self.net(x) class Attention(nn.Module): def __init__( self, dim, heads=8, dropout=0.0, num_keypoints=None, scale_with_head=False, fix_img2motion_attention=False, num_img_tokens=None, ): super().__init__() self.heads = heads self.scale = (dim // heads) ** -0.5 if scale_with_head else dim**-0.5 self.to_qkv = nn.Linear(dim, dim * 3, bias=False) self.to_out = nn.Sequential(nn.Linear(dim, dim), nn.Dropout(dropout)) self.num_keypoints = num_keypoints self.fix_img2motion_attention = fix_img2motion_attention if fix_img2motion_attention: num_tokens = num_keypoints + num_img_tokens mask = torch.zeros(num_tokens, num_tokens) mask[self.num_keypoints :, 0 : self.num_keypoints] += -1000.0 mask = mask.unsqueeze(0).unsqueeze(0) self.register_buffer("attn_mask", mask) # @get_local('dots') # @get_local('attn') def forward(self, x, mask=None): b, n, _, h = *x.shape, self.heads qkv = self.to_qkv(x).chunk(3, dim=-1) q, k, v = map(lambda t: rearrange(t, "b n (h d) -> b h n d", h=h), qkv) dots = torch.einsum("bhid,bhjd->bhij", q, k) * self.scale mask_value = -torch.finfo(dots.dtype).max if mask is not None: mask = F.pad(mask.flatten(1), (1, 0), value=True) assert mask.shape[-1] == dots.shape[-1], "mask has incorrect dimensions" mask = mask[:, None, :] * mask[:, :, None] dots.masked_fill_(~mask, mask_value) del mask if self.fix_img2motion_attention: dots += self.attn_mask attn = dots.softmax(dim=-1) # print(attn[0,0,-1,-1]) out = torch.einsum("bhij,bhjd->bhid", attn, v) out = rearrange(out, "b h n d -> b n (h d)") out = self.to_out(out) return out class QKVAttention(nn.Module): def __init__( self, dim, heads=8, dropout=0.0, num_keypoints=None, scale_with_head=False ): super().__init__() self.heads = heads self.scale = (dim // heads) ** -0.5 if scale_with_head else dim**-0.5 self.to_k = nn.Linear(dim, dim, bias=False) self.to_v = nn.Linear(dim, dim, bias=False) self.to_q = nn.Linear(dim, dim, bias=False) self.to_out = nn.Sequential(nn.Linear(dim, dim), nn.Dropout(dropout)) self.num_keypoints = num_keypoints # @get_local('dots') # @get_local('attn') def forward(self, x, k=None, v=None, mask=None): b, n, _, h = *x.shape, self.heads q = self.to_q(x) k = self.to_k(k) v = self.to_v(v) q = rearrange(q, "b n (h d) -> b h n d", h=h) k = rearrange(k, "b n (h d) -> b h n d", h=h) v = rearrange(v, "b n (h d) -> b h n d", h=h) dots = torch.einsum("bhid,bhjd->bhij", q, k) * self.scale mask_value = -torch.finfo(dots.dtype).max if mask is not None: mask = F.pad(mask.flatten(1), (1, 0), value=True) assert mask.shape[-1] == dots.shape[-1], "mask has incorrect dimensions" mask = mask[:, None, :] * mask[:, :, None] dots.masked_fill_(~mask, mask_value) del mask attn = dots.softmax(dim=-1) # print(attn[0,0,-1,-1]) out = torch.einsum("bhij,bhjd->bhid", attn, v) out = rearrange(out, "b h n d -> b n (h d)") out = self.to_out(out) return out class Transformer(nn.Module): def __init__( self, dim, depth, heads, mlp_dim, dropout, num_keypoints=None, all_attn=False, scale_with_head=False, fix_img2motion_attention=False, num_patches=256, ): super().__init__() self.layers = nn.ModuleList([]) self.all_attn = all_attn self.num_keypoints = num_keypoints self.fix_img2motion_attention = fix_img2motion_attention for d in range(depth): # if not d < depth // 2: # fix_img2motion_attention = False self.layers.append( nn.ModuleList( [ Residual( PreNorm( dim, Attention( dim, heads=heads, dropout=dropout, num_keypoints=num_keypoints, scale_with_head=scale_with_head, fix_img2motion_attention=fix_img2motion_attention, num_img_tokens=num_patches, ), ), num_keypoints=self.num_keypoints, ), Residual( PreNorm(dim, FeedForward(dim, mlp_dim, dropout=dropout)) ), ] ) ) def forward(self, x, mask=None, pos=None): img_token = x[:, self.num_keypoints :] for idx, (attn, ff) in enumerate(self.layers): if idx > 0 and self.all_attn: x[:, self.num_keypoints :] += pos x = attn(x, mask=mask) x = ff(x) return x class TransformerEncoder(nn.Module): def __init__( self, dim, depth, heads, mlp_dim, dropout, num_keypoints=None, all_attn=False, scale_with_head=False, num_patches=256, v_pos=False, ): super().__init__() self.encoder = nn.ModuleList([]) self.all_attn = all_attn self.num_keypoints = num_keypoints self.v_pos = v_pos for d in range(depth): self.encoder.append( nn.ModuleList( [ Residual( QKVPreNorm( dim, QKVAttention( dim, heads=heads, dropout=dropout, num_keypoints=num_keypoints, scale_with_head=scale_with_head, ), ), num_keypoints=self.num_keypoints, ), Residual( PreNorm(dim, FeedForward(dim, mlp_dim, dropout=dropout)) ), ] ) ) def forward(self, x, mask=None, pos=None): for idx, (attn, ff) in enumerate(self.encoder): if self.all_attn: if self.v_pos: x[:, self.num_keypoints :] += pos q = x k = x v = x else: v = x.clone() x[:, self.num_keypoints :] += pos q = x k = x x = attn(q, k=k, v=v, mask=mask) x = ff(x) return x class Bottleneck(nn.Module): expansion = 4 def __init__(self, inplanes, planes, stride=1, downsample=None): super(Bottleneck, self).__init__() self.conv1 = nn.Conv2d(inplanes, planes, kernel_size=1, bias=False) self.bn1 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM) self.conv2 = nn.Conv2d( planes, planes, kernel_size=3, stride=stride, padding=1, bias=False ) self.bn2 = nn.BatchNorm2d(planes, momentum=BN_MOMENTUM) self.conv3 = nn.Conv2d( planes, planes * self.expansion, kernel_size=1, bias=False ) self.bn3 = nn.BatchNorm2d(planes * self.expansion, momentum=BN_MOMENTUM) self.relu = nn.ReLU(inplace=True) self.downsample = downsample self.stride = stride def forward(self, x): residual = x out = self.conv1(x) out = self.bn1(out) out = self.relu(out) out = self.conv2(out) out = self.bn2(out) out = self.relu(out) out = self.conv3(out) out = self.bn3(out) if self.downsample is not None: residual = self.downsample(x) out += residual out = self.relu(out) return out class TokenPose_TB_base(nn.Module): def __init__( self, *, feature_size, patch_size, num_keypoints, dim, depth, heads, mlp_dim, apply_init=False, apply_multi=True, hidden_heatmap_dim=64 * 6, heatmap_dim=64 * 64, heatmap_size=[64, 64], channels=3, dropout=0.0, emb_dropout=0.0, pos_embedding_type="sine-full", estimate_jacobian=True, temperature=0.1, spatial_kp_head=False, jacobian_token=True, hidden_dim=False, affine_jacobian=False, fix_img2motion_attention=False, ): super().__init__() assert isinstance(feature_size, list) and isinstance( patch_size, list ), "image_size and patch_size should be list" assert ( feature_size[0] % patch_size[0] == 0 and feature_size[1] % patch_size[1] == 0 ), "Image dimensions must be divisible by the patch size." num_patches = (feature_size[0] // (patch_size[0])) * ( feature_size[1] // (patch_size[1]) ) patch_dim = channels * patch_size[0] * patch_size[1] # assert pos_embedding_type in ['sine','learnable','sine-full'] self.inplanes = 64 self.patch_size = patch_size self.heatmap_size = heatmap_size self.num_keypoints = num_keypoints self.num_patches = num_patches self.pos_embedding_type = pos_embedding_type self.all_attn = self.pos_embedding_type == "sine-full" self.jacobian_token = jacobian_token if jacobian_token: ## additional token for jacobian num_keypoints = 2 * num_keypoints self.num_keypoints = num_keypoints self.keypoint_token = nn.Parameter(torch.zeros(1, self.num_keypoints, dim)) h, w = ( feature_size[0] // (self.patch_size[0]), feature_size[1] // (self.patch_size[1]), ) self._make_position_embedding(w, h, dim, pos_embedding_type) self.patch_to_embedding = nn.Linear(patch_dim, dim) self.dropout = nn.Dropout(emb_dropout) # transformer self.transformer = Transformer( dim, depth, heads, mlp_dim, dropout, num_keypoints=num_keypoints, all_attn=self.all_attn, scale_with_head=True, fix_img2motion_attention=fix_img2motion_attention, num_patches=num_patches, ) self.to_keypoint_token = nn.Identity() ## original mlphead for keypoint heatmap prediction self.spatial_kp_head = spatial_kp_head if spatial_kp_head: self.mlp_head = ( nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, hidden_heatmap_dim), nn.LayerNorm(hidden_heatmap_dim), nn.Linear(hidden_heatmap_dim, heatmap_dim), ) if (dim <= hidden_heatmap_dim * 0.5 and apply_multi) else nn.Sequential(nn.LayerNorm(dim), nn.Linear(dim, heatmap_dim)) ) self.temperature = temperature ## original mlphead for keypoint heatmap prediction else: if hidden_dim: self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, dim), nn.LayerNorm(dim), nn.Linear(dim, 2), ) else: self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, 2), ) trunc_normal_(self.keypoint_token, std=0.02) if apply_init: self.apply(self._init_weights) ## light-weight head for direct jacobian regression if estimate_jacobian: if hidden_dim: self.mlp_head_jacobian = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, dim), nn.LayerNorm(dim), nn.Linear(dim, 4), ) self._init_weights(self.mlp_head_jacobian[0]) self._init_weights(self.mlp_head_jacobian[1]) self._init_weights(self.mlp_head_jacobian[2]) else: self.mlp_head_jacobian = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, 4), ) self._init_weights(self.mlp_head_jacobian[0]) self.mlp_head_jacobian[-1].weight.data.zero_() self.mlp_head_jacobian[-1].bias.data.copy_( torch.tensor([1, 0, 0, 1], dtype=torch.float) ) self.affine_jacobian = affine_jacobian ## light-weight head for direct keypoint and jacobian regression def _make_position_embedding(self, w, h, d_model, pe_type="sine"): """ d_model: embedding size in transformer encoder """ assert pe_type in ["none", "learnable", "sine", "sine-full"] if pe_type == "none": self.pos_embedding = None print("==> Without any PositionEmbedding~") else: with torch.no_grad(): self.pe_h = h self.pe_w = w length = self.pe_h * self.pe_w if pe_type == "learnable": self.pos_embedding = nn.Parameter( torch.zeros(1, self.num_patches + self.num_keypoints, d_model) ) trunc_normal_(self.pos_embedding, std=0.02) print("==> Add Learnable PositionEmbedding~") else: self.pos_embedding = nn.Parameter( self._make_sine_position_embedding(d_model), requires_grad=False ) print("==> Add Sine PositionEmbedding~") def _make_sine_position_embedding( self, d_model, temperature=10000, scale=2 * math.pi ): h, w = self.pe_h, self.pe_w area = torch.ones(1, h, w) # [b, h, w] y_embed = area.cumsum(1, dtype=torch.float32) x_embed = area.cumsum(2, dtype=torch.float32) one_direction_feats = d_model // 2 eps = 1e-6 y_embed = y_embed / (y_embed[:, -1:, :] + eps) * scale x_embed = x_embed / (x_embed[:, :, -1:] + eps) * scale dim_t = torch.arange(one_direction_feats, dtype=torch.float32) dim_t = temperature ** (2 * (dim_t // 2) / one_direction_feats) pos_x = x_embed[:, :, :, None] / dim_t pos_y = y_embed[:, :, :, None] / dim_t pos_x = torch.stack( (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4 ).flatten(3) pos_y = torch.stack( (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4 ).flatten(3) pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) pos = pos.flatten(2).permute(0, 2, 1) return pos def _make_layer(self, block, planes, blocks, stride=1): downsample = None if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( nn.Conv2d( self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False, ), nn.BatchNorm2d(planes * block.expansion, momentum=BN_MOMENTUM), ) layers = [] layers.append(block(self.inplanes, planes, stride, downsample)) self.inplanes = planes * block.expansion for i in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def _init_weights(self, m): # print("Initialization...") if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=0.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def gaussian2kp(self, heatmap): """ Extract the mean and from a heatmap """ shape = heatmap.shape heatmap = heatmap.unsqueeze(-1) grid = ( make_coordinate_grid(shape[2:], heatmap.type()).unsqueeze_(0).unsqueeze_(0) ) value = (heatmap * grid).sum(dim=(2, 3)) # N * 10 * 2 kp = {"value": value} return kp def forward(self, feature, mask=None): p = self.patch_size # transformer x = rearrange( feature, "b c (h p1) (w p2) -> b (h w) (p1 p2 c)", p1=p[0], p2=p[1] ) # print(x.shape) x = self.patch_to_embedding(x) b, n, _ = x.shape keypoint_tokens = repeat(self.keypoint_token, "() n d -> b n d", b=b) if self.pos_embedding_type in ["sine", "sine-full"]: x += self.pos_embedding[:, :n] x = torch.cat((keypoint_tokens, x), dim=1) elif self.pos_embedding_type == "none": x = torch.cat((keypoint_tokens, x), dim=1) else: x = torch.cat((keypoint_tokens, x), dim=1) x += self.pos_embedding[:, : (n + self.num_keypoints)] x = self.dropout(x) x = self.transformer(x, mask, self.pos_embedding) if self.jacobian_token: x_keypoint = self.to_keypoint_token(x[:, 0 : self.num_keypoints // 2]) else: x_keypoint = self.to_keypoint_token(x[:, 0 : self.num_keypoints]) if self.spatial_kp_head: heatmap = self.mlp_head(x_keypoint) heatmap = F.softmax(heatmap / self.temperature, dim=2) heatmap = rearrange( heatmap, "b c (p1 p2) -> b c p1 p2", p1=self.heatmap_size[0], p2=self.heatmap_size[1], ) out = self.gaussian2kp(heatmap) out["heatmap"] = heatmap else: # keypoint = torch.tanh(self.mlp_head(x_keypoint)) keypoint = 2 * F.sigmoid(self.mlp_head(x_keypoint)) - 1 out = {"kp": keypoint} if self.mlp_head_jacobian is not None: if self.jacobian_token: x_jacobian = self.to_keypoint_token( x[:, self.num_keypoints // 2 : self.num_keypoints] ) else: x_jacobian = x_keypoint jacobian = self.mlp_head_jacobian(x_jacobian) if self.affine_jacobian: theta = jacobian[:, :, 0:2] theta = theta / (torch.norm(theta, p=2, dim=-1, keepdim=True) + 1e-10) cos_theta = theta[:, :, 0:1] sin_theta = theta[:, :, 1:2] rotate = torch.cat( (cos_theta, -sin_theta, sin_theta, cos_theta), dim=-1 ) rotate = rearrange(rotate, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) scale = jacobian[:, :, 2:] # scale = torch.abs(jacobian[:,:,2:]) + 0.1 scale = torch.tanh(scale) * 0.9 + 1 scale = 1 / scale scale_x = scale[:, :, 0:1] scale_y = scale[:, :, 1:2] scale = torch.cat( ( scale_x, torch.zeros(scale_x.shape).to(x.device), torch.zeros(scale_x.shape).to(x.device), scale_y, ), dim=-1, ) scale = rearrange(scale, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) jacobian = torch.matmul(rotate, scale) else: jacobian = rearrange(jacobian, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) out["jacobian"] = jacobian return out class TokenPose_S_base(nn.Module): def __init__( self, *, image_size, patch_size, num_keypoints, dim, depth, heads, mlp_dim, apply_init=False, apply_multi=True, hidden_heatmap_dim=64 * 6, heatmap_dim=64 * 48, heatmap_size=[64, 48], channels=3, dropout=0.0, emb_dropout=0.0, pos_embedding_type="learnable", estimate_jacobian=False, temperature=0.1, jacobian_token=True, hidden_dim=False, affine_jacobian=False, ): super().__init__() assert isinstance(image_size, list) and isinstance( patch_size, list ), "image_size and patch_size should be list" assert ( image_size[0] % patch_size[0] == 0 and image_size[1] % patch_size[1] == 0 ), "Image dimensions must be divisible by the patch size." num_patches = (image_size[0] // (4 * patch_size[0])) * ( image_size[1] // (4 * patch_size[1]) ) patch_dim = channels * patch_size[0] * patch_size[1] assert ( num_patches > MIN_NUM_PATCHES ), f"your number of patches ({num_patches}) is way too small for attention to be effective (at least 16). Try decreasing your patch size" assert pos_embedding_type in ["sine", "none", "learnable", "sine-full"] self.inplanes = 64 self.patch_size = patch_size self.heatmap_size = heatmap_size self.num_keypoints = num_keypoints self.num_patches = num_patches self.pos_embedding_type = pos_embedding_type self.all_attn = self.pos_embedding_type == "sine-full" self.jacobian_token = jacobian_token if jacobian_token: ## additional token for jacobian num_keypoints = 2 * num_keypoints self.num_keypoints = num_keypoints self.keypoint_token = nn.Parameter(torch.zeros(1, self.num_keypoints, dim)) h, w = ( image_size[0] // (4 * self.patch_size[0]), image_size[1] // (4 * self.patch_size[1]), ) self._make_position_embedding(w, h, dim, pos_embedding_type) self.patch_to_embedding = nn.Linear(patch_dim, dim) self.dropout = nn.Dropout(emb_dropout) # stem net self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=2, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(64, momentum=BN_MOMENTUM) self.conv2 = nn.Conv2d(64, 64, kernel_size=3, stride=2, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(64, momentum=BN_MOMENTUM) self.relu = nn.ReLU(inplace=True) self.layer1 = self._make_layer(Bottleneck, 64, 4) # transformer self.transformer = Transformer( dim, depth, heads, mlp_dim, dropout, num_keypoints=num_keypoints, all_attn=self.all_attn, ) self.to_keypoint_token = nn.Identity() if hidden_dim: self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, dim), nn.LayerNorm(dim), nn.Linear(dim, 2), ) else: self.mlp_head = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, 2), ) trunc_normal_(self.keypoint_token, std=0.02) if apply_init: self.apply(self._init_weights) ## light-weight head for direct jacobian regression if estimate_jacobian: if hidden_dim: self.mlp_head_jacobian = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, dim), nn.LayerNorm(dim), nn.Linear(dim, 4), ) self._init_weights(self.mlp_head_jacobian[0]) self._init_weights(self.mlp_head_jacobian[1]) self._init_weights(self.mlp_head_jacobian[2]) else: self.mlp_head_jacobian = nn.Sequential( nn.LayerNorm(dim), nn.Linear(dim, 4), ) self._init_weights(self.mlp_head_jacobian[0]) self.mlp_head_jacobian[-1].weight.data.zero_() self.mlp_head_jacobian[-1].bias.data.copy_( torch.tensor([1, 0, 0, 1], dtype=torch.float) ) self.temperature = temperature self.affine_jacobian = affine_jacobian ## light-weight head for direct keypoint and jacobian regression def _make_position_embedding(self, w, h, d_model, pe_type="sine"): """ d_model: embedding size in transformer encoder """ assert pe_type in ["none", "learnable", "sine", "sine-full"] if pe_type == "none": self.pos_embedding = None print("==> Without any PositionEmbedding~") else: with torch.no_grad(): self.pe_h = h self.pe_w = w length = self.pe_h * self.pe_w if pe_type == "learnable": self.pos_embedding = nn.Parameter( torch.zeros(1, self.num_patches + self.num_keypoints, d_model) ) trunc_normal_(self.pos_embedding, std=0.02) print("==> Add Learnable PositionEmbedding~") else: self.pos_embedding = nn.Parameter( self._make_sine_position_embedding(d_model), requires_grad=False ) print("==> Add Sine PositionEmbedding~") def _make_sine_position_embedding( self, d_model, temperature=10000, scale=2 * math.pi ): h, w = self.pe_h, self.pe_w area = torch.ones(1, h, w) # [b, h, w] y_embed = area.cumsum(1, dtype=torch.float32) x_embed = area.cumsum(2, dtype=torch.float32) one_direction_feats = d_model // 2 eps = 1e-6 y_embed = y_embed / (y_embed[:, -1:, :] + eps) * scale x_embed = x_embed / (x_embed[:, :, -1:] + eps) * scale dim_t = torch.arange(one_direction_feats, dtype=torch.float32) dim_t = temperature ** (2 * (dim_t // 2) / one_direction_feats) pos_x = x_embed[:, :, :, None] / dim_t pos_y = y_embed[:, :, :, None] / dim_t pos_x = torch.stack( (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4 ).flatten(3) pos_y = torch.stack( (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4 ).flatten(3) pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) pos = pos.flatten(2).permute(0, 2, 1) return pos def _make_layer(self, block, planes, blocks, stride=1): downsample = None if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( nn.Conv2d( self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False, ), nn.BatchNorm2d(planes * block.expansion, momentum=BN_MOMENTUM), ) layers = [] layers.append(block(self.inplanes, planes, stride, downsample)) self.inplanes = planes * block.expansion for i in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def _init_weights(self, m): # print("Initialization...") if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=0.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def forward(self, img, mask=None): p = self.patch_size # stem net x = self.conv1(img) x = self.bn1(x) x = self.relu(x) x = self.conv2(x) x = self.bn2(x) x = self.relu(x) x = self.layer1(x) # transformer x = rearrange(x, "b c (h p1) (w p2) -> b (h w) (p1 p2 c)", p1=p[0], p2=p[1]) x = self.patch_to_embedding(x) b, n, _ = x.shape keypoint_tokens = repeat(self.keypoint_token, "() n d -> b n d", b=b) if self.pos_embedding_type in ["sine", "sine-full"]: # x += self.pos_embedding[:, :n] x = torch.cat((keypoint_tokens, x), dim=1) elif self.pos_embedding_type == "learnable": x = torch.cat((keypoint_tokens, x), dim=1) x += self.pos_embedding[:, : (n + self.num_keypoints)] x = self.dropout(x) x = self.transformer(x, mask, self.pos_embedding) x_keypoint = self.to_keypoint_token(x[:, 0 : self.num_keypoints // 2]) keypoint = 2 * F.sigmoid(self.mlp_head(x_keypoint)) - 1 out = {"value": keypoint} if self.mlp_head_jacobian is not None: x_jacobian = self.to_keypoint_token( x[:, self.num_keypoints // 2 : self.num_keypoints] ) # x_jacobian = x_keypoint jacobian = self.mlp_head_jacobian(x_jacobian) if self.affine_jacobian: theta = jacobian[:, :, 0:2] theta = theta / (torch.norm(theta, p=2, dim=-1, keepdim=True) + 1e-10) cos_theta = theta[:, :, 0:1] sin_theta = theta[:, :, 1:2] rotate = torch.cat( (cos_theta, -sin_theta, sin_theta, cos_theta), dim=-1 ) rotate = rearrange(rotate, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) scale = torch.abs(jacobian[:, :, 2:]) + 0.1 # scale = torch.tanh(scale)*0.9+1 # scale = 1/scale scale_x = scale[:, :, 0:1] scale_y = scale[:, :, 1:2] scale = torch.cat( ( scale_x, torch.zeros(scale_x.shape).to(x.device), torch.zeros(scale_x.shape).to(x.device), scale_y, ), dim=-1, ) scale = rearrange(scale, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) jacobian = torch.matmul(rotate, scale) else: # jacobian = rearrange(jacobian,'b c (p0 p1 p2) -> b c p0 p1 p2',p0=4,p1=self.heatmap_size[0],p2=self.heatmap_size[1]) # jacobian = heatmap.unsqueeze(2) * jacobian # jacobian = jacobian.sum(-1).sum(-1) jacobian = rearrange(jacobian, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) out["jacobian"] = jacobian return out class TokenPose_L_base(nn.Module): def __init__( self, *, feature_size, patch_size, num_keypoints, dim, depth, heads, mlp_dim, apply_init=False, hidden_heatmap_dim=64 * 6, heatmap_dim=64 * 48, heatmap_size=[64, 48], channels=3, dropout=0.0, emb_dropout=0.0, pos_embedding_type="learnable", estimate_jacobian=False, temperature=0.1, jacobian_token=True, hidden_dim=False, affine_jacobian=False, ): super().__init__() assert isinstance(feature_size, list) and isinstance( patch_size, list ), "image_size and patch_size should be list" assert ( feature_size[0] % patch_size[0] == 0 and feature_size[1] % patch_size[1] == 0 ), "Image dimensions must be divisible by the patch size." num_patches = (feature_size[0] // (patch_size[0])) * ( feature_size[1] // (patch_size[1]) ) patch_dim = channels * patch_size[0] * patch_size[1] assert pos_embedding_type in ["sine", "learnable", "sine-full"] self.inplanes = 64 self.patch_size = patch_size self.heatmap_size = heatmap_size self.num_keypoints = num_keypoints self.num_patches = num_patches self.pos_embedding_type = pos_embedding_type self.all_attn = self.pos_embedding_type == "sine-full" self.jacobian_token = jacobian_token if jacobian_token: ## additional token for jacobian num_keypoints = 2 * num_keypoints self.num_keypoints = num_keypoints self.keypoint_token = nn.Parameter(torch.zeros(1, self.num_keypoints, dim)) h, w = ( feature_size[0] // (self.patch_size[0]), feature_size[1] // (self.patch_size[1]), ) # for normal self._make_position_embedding(w, h, dim, pos_embedding_type) self.patch_to_embedding = nn.Linear(patch_dim, dim) self.dropout = nn.Dropout(emb_dropout) # transformer self.transformer1 = Transformer( dim, depth, heads, mlp_dim, dropout, num_keypoints=num_keypoints, all_attn=self.all_attn, scale_with_head=True, ) self.transformer2 = Transformer( dim, depth, heads, mlp_dim, dropout, num_keypoints=num_keypoints, all_attn=self.all_attn, scale_with_head=True, ) self.transformer3 = Transformer( dim, depth, heads, mlp_dim, dropout, num_keypoints=num_keypoints, all_attn=self.all_attn, scale_with_head=True, ) self.to_keypoint_token = nn.Identity() if hidden_dim: self.mlp_head = nn.Sequential( # nn.LayerNorm(dim), # nn.Linear(dim, dim), # nn.LayerNorm(dim), # nn.Linear(dim, 2), nn.LayerNorm(dim * 3), nn.Linear(dim * 3, dim * 3), nn.LayerNorm(dim * 3), nn.Linear(dim * 3, 2), ) else: self.mlp_head = nn.Sequential( # nn.LayerNorm(dim), # nn.Linear(dim, 2), nn.LayerNorm(dim * 3), nn.Linear(dim * 3, 2), ) trunc_normal_(self.keypoint_token, std=0.02) if apply_init: self.apply(self._init_weights) ## light-weight head for direct jacobian regression if estimate_jacobian: if hidden_dim: self.mlp_head_jacobian = nn.Sequential( # nn.LayerNorm(dim), # nn.Linear(dim, dim), # nn.LayerNorm(dim), # nn.Linear(dim, 4), nn.LayerNorm(dim * 3), nn.Linear(dim * 3, dim * 3), nn.LayerNorm(dim * 3), nn.Linear(dim * 3, 4), ) self._init_weights(self.mlp_head_jacobian[0]) self._init_weights(self.mlp_head_jacobian[1]) self._init_weights(self.mlp_head_jacobian[2]) else: self.mlp_head_jacobian = nn.Sequential( # nn.LayerNorm(dim), # nn.Linear(dim, 4), nn.LayerNorm(dim * 3), nn.Linear(dim * 3, 4), ) self._init_weights(self.mlp_head_jacobian[0]) self.mlp_head_jacobian[-1].weight.data.zero_() self.mlp_head_jacobian[-1].bias.data.copy_( torch.tensor([1, 0, 0, 1], dtype=torch.float) ) self.temperature = temperature self.affine_jacobian = affine_jacobian ## light-weight head for direct keypoint and jacobian regression def _make_position_embedding(self, w, h, d_model, pe_type="sine"): """ d_model: embedding size in transformer encoder """ assert pe_type in ["none", "learnable", "sine", "sine-full"] if pe_type == "none": self.pos_embedding = None print("==> Without any PositionEmbedding~") else: with torch.no_grad(): self.pe_h = h self.pe_w = w length = self.pe_h * self.pe_w if pe_type == "learnable": self.pos_embedding = nn.Parameter( torch.zeros(1, self.num_patches + self.num_keypoints, d_model) ) trunc_normal_(self.pos_embedding, std=0.02) print("==> Add Learnable PositionEmbedding~") else: self.pos_embedding = nn.Parameter( self._make_sine_position_embedding(d_model), requires_grad=False ) print("==> Add Sine PositionEmbedding~") def _make_sine_position_embedding( self, d_model, temperature=10000, scale=2 * math.pi ): h, w = self.pe_h, self.pe_w area = torch.ones(1, h, w) # [b, h, w] y_embed = area.cumsum(1, dtype=torch.float32) x_embed = area.cumsum(2, dtype=torch.float32) one_direction_feats = d_model // 2 eps = 1e-6 y_embed = y_embed / (y_embed[:, -1:, :] + eps) * scale x_embed = x_embed / (x_embed[:, :, -1:] + eps) * scale dim_t = torch.arange(one_direction_feats, dtype=torch.float32) dim_t = temperature ** (2 * (dim_t // 2) / one_direction_feats) pos_x = x_embed[:, :, :, None] / dim_t pos_y = y_embed[:, :, :, None] / dim_t pos_x = torch.stack( (pos_x[:, :, :, 0::2].sin(), pos_x[:, :, :, 1::2].cos()), dim=4 ).flatten(3) pos_y = torch.stack( (pos_y[:, :, :, 0::2].sin(), pos_y[:, :, :, 1::2].cos()), dim=4 ).flatten(3) pos = torch.cat((pos_y, pos_x), dim=3).permute(0, 3, 1, 2) pos = pos.flatten(2).permute(0, 2, 1) return pos def _make_layer(self, block, planes, blocks, stride=1): downsample = None if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( nn.Conv2d( self.inplanes, planes * block.expansion, kernel_size=1, stride=stride, bias=False, ), nn.BatchNorm2d(planes * block.expansion, momentum=BN_MOMENTUM), ) layers = [] layers.append(block(self.inplanes, planes, stride, downsample)) self.inplanes = planes * block.expansion for i in range(1, blocks): layers.append(block(self.inplanes, planes)) return nn.Sequential(*layers) def _init_weights(self, m): # print("Initialization...") if isinstance(m, nn.Linear): trunc_normal_(m.weight, std=0.02) if isinstance(m, nn.Linear) and m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.LayerNorm): nn.init.constant_(m.bias, 0) nn.init.constant_(m.weight, 1.0) def forward(self, feature, mask=None): p = self.patch_size # transformer x = rearrange( feature, "b c (h p1) (w p2) -> b (h w) (p1 p2 c)", p1=p[0], p2=p[1] ) x = self.patch_to_embedding(x) b, n, _ = x.shape keypoint_tokens = repeat(self.keypoint_token, "() n d -> b n d", b=b) if self.pos_embedding_type in ["sine", "sine-full"]: x += self.pos_embedding[:, :n] x = torch.cat((keypoint_tokens, x), dim=1) else: x = torch.cat((keypoint_tokens, x), dim=1) x += self.pos_embedding[:, : (n + self.num_keypoints)] x = self.dropout(x) x1 = self.transformer1(x, mask, self.pos_embedding) x2 = self.transformer2(x1, mask, self.pos_embedding) x3 = self.transformer3(x2, mask, self.pos_embedding) x1_out = self.to_keypoint_token(x1[:, 0 : self.num_keypoints]) x2_out = self.to_keypoint_token(x2[:, 0 : self.num_keypoints]) x3_out = self.to_keypoint_token(x3[:, 0 : self.num_keypoints]) x = torch.cat((x1_out, x2_out, x3_out), dim=2) # x = x3 if self.jacobian_token: x_keypoint = self.to_keypoint_token(x[:, 0 : self.num_keypoints // 2]) else: x_keypoint = self.to_keypoint_token(x[:, 0 : self.num_keypoints]) keypoint = 2 * F.sigmoid(self.mlp_head(x_keypoint)) - 1 out = {"value": keypoint} if self.mlp_head_jacobian is not None: if self.jacobian_token: x_jacobian = self.to_keypoint_token( x[:, self.num_keypoints // 2 : self.num_keypoints] ) else: x_jacobian = x_keypoint jacobian = self.mlp_head_jacobian(x_jacobian) if self.affine_jacobian: theta = jacobian[:, :, 0:2] theta = theta / (torch.norm(theta, p=2, dim=-1, keepdim=True) + 1e-10) cos_theta = theta[:, :, 0:1] sin_theta = theta[:, :, 1:2] rotate = torch.cat( (cos_theta, -sin_theta, sin_theta, cos_theta), dim=-1 ) rotate = rearrange(rotate, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) scale = torch.abs(jacobian[:, :, 2:]) + 0.1 # scale = torch.tanh(scale)*0.9+1 # scale = 1/scale scale_x = scale[:, :, 0:1] scale_y = scale[:, :, 1:2] scale = torch.cat( ( scale_x, torch.zeros(scale_x.shape).to(x.device), torch.zeros(scale_x.shape).to(x.device), scale_y, ), dim=-1, ) scale = rearrange(scale, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) jacobian = torch.matmul(rotate, scale) else: # jacobian = rearrange(jacobian,'b c (p0 p1 p2) -> b c p0 p1 p2',p0=4,p1=self.heatmap_size[0],p2=self.heatmap_size[1]) # jacobian = heatmap.unsqueeze(2) * jacobian # jacobian = jacobian.sum(-1).sum(-1) jacobian = rearrange(jacobian, "b c (p1 p2) -> b c p1 p2", p1=2, p2=2) out["jacobian"] = jacobian return out