Spaces:
Running
Running
| # Copyright (c) 2025 Bytedance Ltd. and/or its affiliates | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import torch | |
| from torch import nn, einsum | |
| import torch.nn.functional as F | |
| from skinning_models.networks import PreNorm, FeedForward, Attention, PointEmbed | |
| from third_partys.Michelangelo.encode import load_model | |
| from third_partys.PartField.encode import partfield | |
| class SkinningBlock(nn.Module): | |
| def __init__(self, dim, args, heads=8, dim_head=64, ff_mult=4): | |
| super().__init__() | |
| # -- Self-Attn (joint) | |
| self.self_attn_j = PreNorm(dim, Attention(dim, dim, heads=heads, dim_head=dim_head), context_dim = dim) | |
| self.ff_j = PreNorm(dim, FeedForward(dim, mult=ff_mult)) | |
| # -- Cross-Attn (point -> shape) | |
| self.cross_attn_ps = PreNorm(dim, Attention(dim, dim, heads=heads, dim_head=dim_head), context_dim = dim) | |
| self.ff_ps = PreNorm(dim, FeedForward(dim, mult=ff_mult)) | |
| # -- Cross-Attn (joint -> shape) | |
| self.cross_attn_js = PreNorm(dim, Attention(dim, dim, heads=heads, dim_head=dim_head), context_dim = dim) | |
| self.ff_js = PreNorm(dim, FeedForward(dim, mult=ff_mult)) | |
| # -- Cross-Attn (joint -> point) | |
| self.cross_attn_jp = PreNorm(dim, Attention(dim, dim, heads=heads, dim_head=dim_head), context_dim = dim) | |
| self.ff_jp = PreNorm(dim, FeedForward(dim, mult=ff_mult)) | |
| # -- Cross-Attn (point -> joint) | |
| self.cross_attn_pj = PreNorm(dim, Attention(dim, dim, heads=heads, dim_head=dim_head), context_dim = dim) | |
| self.ff_pj = PreNorm(dim, FeedForward(dim, mult=ff_mult)) | |
| self.use_TAJA = args.use_TAJA | |
| if self.use_TAJA: | |
| self.rel_pos_embedding = nn.Embedding(10, dim // 4) | |
| self.rel_pos_proj = nn.Linear(dim // 4, heads) | |
| self.rel_pos_scale = nn.Parameter(torch.ones(1) * 0.1) # Initial value 0.1 | |
| def forward(self, point, joint, shape, valid_mask=None, graph_dist=None): | |
| """ | |
| point: (B, Np, dim) | |
| joint: (B, Nj, dim) | |
| shape: (B, Ns, dim) | |
| valid_mask: (B, Nj) or None | |
| return: | |
| updated_point, updated_joint | |
| """ | |
| # 1) joint self-attention with TAJA | |
| if self.use_TAJA: | |
| batch_size, n_joints = joint.shape[0], joint.shape[1] | |
| dist_mask = valid_mask.unsqueeze(1) & valid_mask.unsqueeze(2) | |
| safe_dist = torch.where(dist_mask, graph_dist, torch.zeros_like(graph_dist)) | |
| distances_clamped = torch.clamp(safe_dist, 0, 9).long() # (B, Nj, Nj) | |
| rel_pos_embeddings = self.rel_pos_embedding(distances_clamped) # (B, Nj, Nj, dim//4) | |
| rel_pos_encoding = self.rel_pos_proj(rel_pos_embeddings) * self.rel_pos_scale # (B, Nj, Nj, heads) | |
| rel_pos_encoding = torch.where( | |
| dist_mask.unsqueeze(-1).expand_as(rel_pos_encoding), | |
| rel_pos_encoding, | |
| torch.zeros_like(rel_pos_encoding) | |
| ) | |
| else: | |
| rel_pos_encoding = None | |
| joint_enhance = self.self_attn_j(joint, context=joint, context_mask=valid_mask, rel_pos=rel_pos_encoding) + joint | |
| joint_enhance = self.ff_j(joint_enhance) + joint_enhance | |
| # 2) point->shape | |
| point_context = self.cross_attn_ps(point, context=shape) + point | |
| point_context = self.ff_ps(point_context) + point_context | |
| # 2) joint->shape | |
| joint_context = self.cross_attn_js(joint_enhance, context=shape, query_mask=valid_mask) + joint_enhance | |
| joint_context = self.ff_js(joint_context) + joint_context | |
| # 3) joint->point | |
| joint_refine = self.cross_attn_jp(joint_context, context=point_context, query_mask=valid_mask) + joint_context | |
| joint_refine = self.ff_jp(joint_refine) + joint_refine | |
| # 4) point->joint | |
| point_final = self.cross_attn_pj(point_context, context=joint_refine, context_mask=valid_mask) + point_context | |
| point_final = self.ff_pj(point_final) + point_final | |
| return point_final, joint_refine | |
| class SkinningNetStacked(nn.Module): | |
| def __init__(self, args, dim=768, heads=8, dim_head=64, ff_mult=4, scale_init=1.): | |
| super().__init__() | |
| self.args = args | |
| self.max_joints = args.max_joints | |
| self.skeleton_condition = PointEmbed(dim=dim) | |
| self.scale = nn.Parameter(torch.tensor(scale_init), requires_grad=True) | |
| self.point_encoder = load_model() | |
| self.point_encoder.eval() | |
| for param in self.point_encoder.parameters(): | |
| param.requires_grad = False | |
| self.point_embed_pe = PointEmbed(dim=dim) | |
| self.point_embed = partfield() | |
| self.proj = nn.Sequential( | |
| nn.Linear(448, dim), | |
| nn.SiLU(), | |
| nn.Linear(dim, dim), | |
| ) | |
| # multiple blocks | |
| self.blocks = nn.ModuleList([ | |
| SkinningBlock(dim, args, heads=heads, dim_head=dim_head, ff_mult=ff_mult) | |
| for _ in range(args.depth) | |
| ]) | |
| def process_point_feature(self, point_feature): | |
| shape_latents = self.point_encoder.to_shape_latents(point_feature[:, 1:]) | |
| point_feature_first_column = point_feature[:, 0:1] | |
| encode_feature = torch.cat([point_feature_first_column, shape_latents], dim=1) | |
| return encode_feature | |
| def forward(self, | |
| sample_points, skeleton, pc_w_norm, dist_graph, | |
| valid_mask=None, # (B, Nj) | |
| target=None): # (B, Np, Nj) | |
| """ | |
| cosine similarity + softmax + loss | |
| """ | |
| point_out1 = self.point_embed(sample_points) | |
| point_out1 = self.proj(point_out1) | |
| point_out2 = self.point_embed_pe(sample_points) | |
| point_out = point_out1 + point_out2 # (bs, 8192, 768) | |
| joint_out = self.skeleton_condition(skeleton) # (bs, args.max_joints, 768) | |
| point_feature = self.point_encoder.encode_latents(pc_w_norm) | |
| shape_feature = self.process_point_feature(point_feature=point_feature) # (bs, 257, 768) | |
| for block in self.blocks: | |
| point_out, joint_out = block(point_out, joint_out, shape_feature, valid_mask=valid_mask, graph_dist=dist_graph) | |
| point_norm = F.normalize(point_out, p=2, dim=-1) # (B, Np, D) | |
| joint_norm = F.normalize(joint_out, p=2, dim=-1) # (B, Nj, D) | |
| score_cos = einsum('b i d, b j d -> b i j', point_norm, joint_norm) | |
| score = (self.scale.abs()+1e-9) * score_cos | |
| skinning_weight = F.softmax(score, dim=-1) | |
| if target is None: | |
| return skinning_weight | |