| import torch |
| import torch.nn as nn |
| from torch.nn import functional as F |
| from nets.smpler_x import PositionNet, HandRotationNet, FaceRegressor, BoxNet, HandRoI, BodyRotationNet, BodyTokenNet, GlobalHandPositionNet, HandTokenRegressor |
| from tokenhmr.lib.models.heads.token_head import SMPLTokenDecoderHead |
| from nets.loss import CoordLoss, ParamLoss, CELoss |
| from utils.human_models import smpl_x, smpl |
| from utils.transforms import rot6d_to_axis_angle, restore_bbox |
| from config import cfg |
| import math |
| import copy |
| from mmpose.models import build_posenet |
| from mmcv import Config |
| from FishEyeCalibrated import FishEyeCameraCalibrated |
| import numpy as np |
| import cv2 |
| import matplotlib.pyplot as plt |
| from pdb import set_trace |
| |
| from scipy.spatial import cKDTree |
| from data import humandata |
| import os |
| from omegaconf import OmegaConf |
| from DPoserX.run.tester.wholebody.smplify import DPoser |
| from torch.autograd import Function |
|
|
|
|
| |
| |
| |
| |
|
|
| class GradReverse(Function): |
| @staticmethod |
| def forward(ctx, x, lambd): |
| ctx.lambd = float(lambd) |
| return x.view_as(x) |
| @staticmethod |
| def backward(ctx, grad_output): |
| return grad_output.neg() * ctx.lambd, None |
| |
|
|
| bone_pairs = [ |
| [0, 1], [1, 2], [2, 3], |
| [0, 4], [4, 5], [5, 6], |
| [1, 7], [7, 8], [8, 9], [9,10], |
| [4,11], [11,12], [12,13], [13,14], |
| ] |
|
|
| mo2cap2_to_smplx = { |
| "neck": 7, |
| "right_shoulder": 9, |
| "right_elbow": 11, |
| "right_wrist": 13, |
| "left_shoulder": 8, |
| "left_elbow": 10, |
| "left_wrist": 12, |
| "right_hip": 2, |
| "right_knee": 4, |
| "right_ankle": 6, |
| "right_foot": 19, |
| "left_hip": 1, |
| "left_knee": 3, |
| "left_ankle": 5, |
| "left_foot": 16, |
| } |
|
|
| mo2cap2_joint_names = [ |
| "neck","right_shoulder","right_elbow","right_wrist", |
| "left_shoulder","left_elbow","left_wrist", |
| "right_hip","right_knee","right_ankle","right_foot", |
| "left_hip","left_knee","left_ankle","left_foot", |
| ] |
| mo2cap2_chain = [ |
| [0, 1, 2, 3], |
| [0, 4, 5, 6], |
| [1, 7, 8, 9, 10], |
| [4, 11, 12, 13, 14], |
| [7, 11], |
| ] |
|
|
|
|
| class Model(nn.Module): |
| def __init__(self, encoder, token_decoder, body_position_net, body_rotation_net, box_net, hand_position_net, hand_roi_net, |
| hand_rotation_net, face_regressor, mode): |
| super(Model, self).__init__() |
|
|
| if getattr(cfg, 'fisheye_camera_path', False): |
| fisheye_camera_path = cfg.fisheye_camera_path |
| else: |
| fisheye_camera_path = '/home/cv1/works/SMPLer-X/main/fisheye.calibration_01_12.json' |
| self.fisheye_camera = FishEyeCameraCalibrated(fisheye_camera_path) |
| |
| |
| self.encoder = encoder |
|
|
| self.box_net = box_net |
| |
| |
| self.hand_roi_net = hand_roi_net |
| self.hand_position_net = hand_position_net |
| self.hand_regressor = hand_rotation_net |
| self.neck = [self.box_net, self.hand_roi_net] |
| |
| self.face_regressor = face_regressor |
|
|
| self.smplx_layer = copy.deepcopy(smpl_x.layer['neutral']).cuda() |
| self.smpl_layer = copy.deepcopy(smpl.layer['neutral']).cuda() |
|
|
| self.coord_loss = CoordLoss() |
| self.param_loss = ParamLoss() |
| self.ce_loss = CELoss() |
|
|
| self.body_num_joints = len(smpl_x.pos_joint_part['body']) |
| self.hand_joint_num = len(smpl_x.pos_joint_part['rhand']) |
|
|
| self.body_position_net = body_position_net |
| if getattr(cfg, 'use_token_decoder', False): |
| if getattr(cfg, 'use_smpl', False): |
| self.token_decoder = token_decoder |
| |
|
|
| self.head = [self.token_decoder, self.body_position_net, |
| self.hand_position_net, self.hand_regressor, |
| self.face_regressor] |
|
|
| self.trainable_modules = [self.encoder, self.token_decoder, self.body_position_net] |
| else: |
| self.token_decoder = token_decoder |
| |
|
|
| self.head = [self.token_decoder, self.body_position_net, |
| self.hand_position_net, self.hand_regressor, |
| self.face_regressor] |
|
|
| self.trainable_modules = [self.encoder, self.token_decoder, self.body_position_net, |
| self.box_net, self.hand_position_net, |
| self.hand_roi_net, self.hand_regressor, self.face_regressor] |
|
|
| else: |
| if getattr(cfg, 'use_smpl', False): |
| |
| self.body_regressor = body_rotation_net |
| self.head = [self.body_position_net, self.body_regressor, |
| self.hand_position_net, self.hand_regressor, |
| self.face_regressor] |
| |
| if getattr(cfg, 'use_dposerx', False): |
| if mode == 'train': |
| self.dposer_x = DPoser(batch_size=cfg.train_batch_size, config_path=cfg.dposerx_cfg_path).cuda() |
| self.trainable_modules = [self.encoder, self.dposer_x, self.body_position_net, self.body_regressor] |
| |
| else: |
| self.dposer_x = DPoser(batch_size=cfg.test_batch_size, config_path=cfg.dposerx_cfg_path).cuda() |
| |
| else: |
| self.trainable_modules = [self.encoder, self.body_position_net, self.body_regressor] |
|
|
| else: |
| |
| self.body_regressor = body_rotation_net |
| self.head = [self.body_position_net, self.body_regressor, |
| self.hand_position_net, self.hand_regressor, |
| self.face_regressor] |
|
|
| if getattr(cfg, 'use_dposerx', False): |
| if mode == 'train': |
| self.dposer_x = DPoser(batch_size=cfg.train_batch_size, config_path=cfg.dposerx_cfg_path).cuda() |
|
|
| self.trainable_modules = [self.encoder, self.dposer_x, self.body_position_net, self.body_regressor, |
| self.box_net, self.hand_position_net, |
| self.hand_roi_net, self.hand_regressor] |
| |
| else: |
| self.dposer_x = DPoser(batch_size=cfg.test_batch_size, config_path=cfg.dposerx_cfg_path).cuda() |
| |
| else: |
| self.trainable_modules = [self.encoder, self.body_position_net, self.body_regressor, |
| self.box_net, self.hand_position_net, |
| self.hand_roi_net, self.hand_regressor, self.face_regressor] |
| |
| |
| self.special_trainable_modules = [] |
| |
| |
| self.id_adv_lambda = float(getattr(cfg, 'id_adv_lambda', 0.0)) |
| self.num_subjects = int(getattr(cfg, 'num_subjects', 0)) |
| if self.id_adv_lambda > 0 and self.num_subjects > 0: |
| |
| self.id_head = nn.Linear(cfg.feat_dim, self.num_subjects) |
| self.id_loss_fn = nn.BCEWithLogitsLoss() |
| |
| self.trainable_modules.append(self.id_head) |
| self.head.append(self.id_head) |
|
|
| |
| param_bb = sum(p.numel() for p in self.encoder.parameters() if p.requires_grad) |
| |
| param_neck = 0 |
| for module in self.neck: |
| param_neck += sum(p.numel() for p in module.parameters() if p.requires_grad) |
| |
| param_head = 0 |
| for module in self.head: |
| param_head += sum(p.numel() for p in module.parameters() if p.requires_grad) |
|
|
| param_net = param_bb + param_neck + param_head |
|
|
| |
| total_params = sum(p.numel() for p in self.parameters()) |
| total_trainable = sum(p.numel() for p in self.parameters() if p.requires_grad) |
|
|
| def _m(x): |
| return x / 1e6 |
|
|
| print("========== SMPLer-X Parameter Counts ==========") |
| print(f"Backbone (encoder, trainable) : {param_bb:,} ({_m(param_bb):.3f} M)") |
| print(f"Neck (box + hand_roi) : {param_neck:,} ({_m(param_neck):.3f} M)") |
| print(f"Head (body/hand/face 등) : {param_head:,} ({_m(param_head):.3f} M)") |
| print("----------------------------------------------") |
| print(f"Total trainable : {param_net:,} ({_m(param_net):.3f} M)") |
| print(f"Total (all params) : {total_params:,} ({_m(total_params):.3f} M)") |
| print("==============================================") |
|
|
|
|
| |
|
|
| self.undist_w, self.undist_h = 1280, 1024 |
| fov_deg = 120 |
| fov_rad = np.deg2rad(fov_deg) |
|
|
| x = np.linspace(-1, 1, self.undist_w) |
| y = np.linspace(-1, 1, self.undist_h) |
| xx, yy = np.meshgrid(x, y) |
| z = 1.0 / np.tan(fov_rad / 2) |
| rays = np.stack([xx, yy, np.full_like(xx, z)], axis=-1) |
| rays /= np.linalg.norm(rays, axis=-1, keepdims=True) |
| rays_flat = rays.reshape(-1, 3) |
|
|
| mapped_pixels = [] |
| for r in rays_flat: |
| try: |
| uv = self.fisheye_camera.world2camera(np.array([r])) |
| mapped_pixels.append(uv[0]) |
| except: |
| mapped_pixels.append([-1, -1]) |
|
|
| self.mapped_pixels = np.array(mapped_pixels, dtype=np.float32).reshape(self.undist_h, self.undist_w, 2) |
|
|
| def _get_subject_scene_dir(self, img_path): |
| """ |
| img_path: .../{subject}/{scene}/imgs/filename 형태라고 가정하고 |
| 마지막 'imgs' 기준으로 바로 앞의 두 디렉토리(subject, scene)를 리턴. |
| 실패하면 빈 문자열 반환. |
| """ |
| if not isinstance(img_path, str): |
| return "" |
| path = img_path.replace("\\", "/") |
| parts = path.split("/") |
|
|
| |
| idx = None |
| for j in range(len(parts) - 1, -1, -1): |
| if parts[j] == "imgs": |
| idx = j |
| break |
|
|
| if idx is None or idx < 2: |
| return "" |
|
|
| subject = parts[idx - 2] |
| scene = parts[idx - 1] |
| return os.path.join(subject, scene) |
|
|
| |
| def _save_activation_heatmap( |
| self, |
| img_3chw: torch.Tensor, |
| feat_3chw: torch.Tensor, |
| save_path: str, |
| alpha: float = 0.45, |
| |
| orig_grid_hw=(16, 16), |
| crop=(0, 2, 0, 2), |
| upsample: str = "nearest" |
| ): |
| """ |
| img_3chw : [3,H,W] |
| feat_3chw: [C,h,w] (현재 16x12 토큰 그리드로 재배치된 feature) |
| """ |
| import os |
| import matplotlib.pyplot as plt |
| import torch.nn.functional as F |
|
|
| |
| hm = feat_3chw.detach().float().mean(0) |
|
|
| |
| hm = (hm - hm.min()) / (hm.max() - hm.min() + 1e-6) |
|
|
| |
| H0, W0 = orig_grid_hw |
| t, r, b, l = crop |
| cam_pad = torch.zeros((H0, W0), dtype=hm.dtype, device=hm.device) |
| alpha_pad = torch.zeros_like(cam_pad) |
| cam_pad[t:H0-b, l:W0-r] = hm |
| alpha_pad[t:H0-b, l:W0-r] = 1.0 |
|
|
| |
| H_img, W_img = img_3chw.shape[-2], img_3chw.shape[-1] |
| cam_up = F.interpolate(cam_pad[None, None], size=(H_img, W_img), |
| mode=upsample, align_corners=False if upsample!="nearest" else None)[0, 0].cpu().numpy() |
| a_up = F.interpolate(alpha_pad[None, None], size=(H_img, W_img), |
| mode="nearest")[0, 0].cpu().numpy() |
|
|
| |
| img = img_3chw.detach().cpu().float().clamp(0, 1).permute(1, 2, 0).numpy() |
| plt.figure(figsize=(6, 4.5)) |
| plt.imshow(img) |
| plt.imshow(cam_up, cmap="viridis", alpha=alpha * a_up) |
| plt.axis("off"); plt.tight_layout() |
| os.makedirs(os.path.dirname(save_path) or ".", exist_ok=True) |
| plt.savefig(save_path, dpi=200, bbox_inches="tight") |
| plt.close() |
|
|
| def visualize_hand_bboxes_on_input(self, img_batch, lhand_bbox, rhand_bbox, |
| save_dir, meta_info=None): |
| """ |
| img_batch : [B, 3, H, W] (inputs['img_ori']) |
| lhand_bbox, rhand_bbox : [B, 4] (xyxy, input_img_shape 기준) |
| """ |
| os.makedirs(save_dir, exist_ok=True) |
|
|
| |
| img_np = img_batch.detach().cpu().numpy().transpose(0, 2, 3, 1) |
|
|
| B = img_np.shape[0] |
| for i in range(B): |
| img = img_np[i] |
|
|
| |
| if img.max() <= 1.0: |
| img = (img * 255.0).astype(np.uint8) |
| else: |
| img = img.astype(np.uint8) |
| vis = img.copy() |
|
|
| h, w = vis.shape[:2] |
|
|
| def _draw_box(box, color, label_text=None): |
| box = box.detach().cpu().numpy() |
| x1, y1, x2, y2 = box |
|
|
| x1 = int(np.clip(x1, 0, w - 1)) |
| x2 = int(np.clip(x2, 0, w - 1)) |
| y1 = int(np.clip(y1, 0, h - 1)) |
| y2 = int(np.clip(y2, 0, h - 1)) |
|
|
| cv2.rectangle(vis, (x1, y1), (x2, y2), color, 2) |
| if label_text is not None: |
| cv2.putText(vis, label_text, (x1, max(0, y1 - 5)), |
| cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1, |
| lineType=cv2.LINE_AA) |
|
|
| |
| _draw_box(lhand_bbox[i], (0, 255, 0), 'L-hand') |
| _draw_box(rhand_bbox[i], (255, 0, 0), 'R-hand') |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
| img_path_i = None |
| sub_rel = "" |
| if meta_info is not None and 'img_path' in meta_info: |
| img_path_i = meta_info['img_path'][i] |
| if isinstance(img_path_i, str): |
| sub_rel = self._get_subject_scene_dir(img_path_i) |
| name = os.path.splitext(os.path.basename(img_path_i))[0] |
| else: |
| name = f"{i:06d}" |
| else: |
| name = f"{i:06d}" |
|
|
| if sub_rel: |
| cur_dir = os.path.join(save_dir, sub_rel) |
| else: |
| cur_dir = save_dir |
|
|
| os.makedirs(cur_dir, exist_ok=True) |
|
|
| out_path = os.path.join(cur_dir, f"{name}_hand_bbox.png") |
| cv2.imwrite(out_path, vis[:, :, ::-1]) |
|
|
|
|
| def get_camera_trans(self, cam_param): |
| |
| t_xy = cam_param[:, :2] |
| gamma = torch.sigmoid(cam_param[:, 2]) |
| k_value = torch.FloatTensor([math.sqrt(cfg.focal[0] * cfg.focal[1] * cfg.camera_3d_size * cfg.camera_3d_size / ( |
| cfg.input_img_shape[0] * cfg.input_img_shape[1]))]).cuda().view(-1) |
| t_z = k_value * gamma |
| cam_trans = torch.cat((t_xy, t_z[:, None]), 1) |
| return cam_trans |
|
|
|
|
| def undistort_tensor_image(self, tensor_img, fisheye_camera: FishEyeCameraCalibrated): |
| """ |
| tensor_img: [B, C, H, W] - torch.Tensor (range 0~1 or 0~255) |
| return: undistorted image in torch.Tensor [B, C, H, W] |
| """ |
| device = tensor_img.device |
| B, C, H, W = tensor_img.shape |
| tensor_img_np = tensor_img.detach().cpu().numpy().transpose(0, 2, 3, 1) |
|
|
| undistorted_np = [] |
| for i in range(B): |
| img = (tensor_img_np[i] * 255).astype(np.uint8) if tensor_img_np[i].max() <= 1 else tensor_img_np[i].astype(np.uint8) |
|
|
| |
| x = np.linspace(-1, 1, W) |
| y = np.linspace(-1, 1, H) |
| xx, yy = np.meshgrid(x, y) |
| z = 1.0 |
| rays = np.stack([xx, yy, np.full_like(xx, z)], axis=-1) |
| rays /= np.linalg.norm(rays, axis=-1, keepdims=True) |
| rays_flat = rays.reshape(-1, 3) |
|
|
| mapped_pixels = [] |
| for r in rays_flat: |
| try: |
| uv = self.fisheye_camera.world2camera(np.array([r])) |
| mapped_pixels.append(uv[0]) |
| except: |
| mapped_pixels.append([-1, -1]) |
| mapped_pixels = np.array(mapped_pixels, dtype=np.float32).reshape(H, W, 2) |
|
|
| undistorted_img = cv2.remap( |
| img, |
| mapped_pixels[:, :, 0], |
| mapped_pixels[:, :, 1], |
| interpolation=cv2.INTER_LINEAR, |
| borderMode=cv2.BORDER_CONSTANT, |
| borderValue=0 |
| ) |
| undistorted_np.append(undistorted_img) |
|
|
| undistorted_np = np.stack(undistorted_np).astype(np.float32) / 255.0 |
| undistorted_tensor = torch.from_numpy(undistorted_np).permute(0, 3, 1, 2).to(device) |
| return undistorted_tensor |
| |
|
|
|
|
|
|
| def visualize_body_img_comparison(self, original, undistorted): |
| """ |
| original, undistorted: [B, C, H, W] torch.Tensor |
| """ |
| import matplotlib.pyplot as plt |
|
|
| B = original.shape[0] |
| for i in range(B): |
| orig_np = original[i].detach().cpu().permute(1, 2, 0).numpy() |
| undist_np = undistorted[i].detach().cpu().permute(1, 2, 0).numpy() |
|
|
| plt.figure(figsize=(10, 5)) |
| plt.subplot(1, 2, 1) |
| plt.imshow(np.clip(orig_np, 0, 1)) |
| plt.title('Original (Fisheye)') |
| plt.axis('off') |
|
|
| plt.subplot(1, 2, 2) |
| plt.imshow(np.clip(undist_np, 0, 1)) |
| plt.title('Undistorted') |
| plt.axis('off') |
|
|
| plt.tight_layout() |
| plt.show() |
|
|
| def get_coord(self, root_pose, body_pose, lhand_pose, rhand_pose, jaw_pose, shape, expr, cam_trans, mode): |
| batch_size = root_pose.shape[0] |
| |
| zero_pose = torch.zeros((1, 3)).float().cuda().repeat(batch_size, 1) |
| |
| |
| |
| |
| |
| |
| |
| output = self.smplx_layer(betas=shape, body_pose=body_pose, global_orient=root_pose, right_hand_pose=rhand_pose, |
| left_hand_pose=lhand_pose, jaw_pose=jaw_pose, leye_pose=zero_pose, |
| reye_pose=zero_pose, expression=expr) |
|
|
| mesh_cam = output.vertices + cam_trans[:, None, :] |
|
|
| |
| |
| |
|
|
| mesh_cam = output.vertices |
| if mode == 'test' and cfg.testset == 'AGORA': |
| joint_cam = output.joints |
| else: |
| joint_cam = output.joints[:, smpl_x.joint_idx, :] |
| |
| |
|
|
|
|
| B, J, _ = joint_cam.shape |
| |
| joint_cam_translation =joint_cam.detach() + cam_trans[:, None, :] |
|
|
| joint_cam_flat = (joint_cam_translation).view(-1, 3) |
|
|
| |
| joint_proj_flat = self.fisheye_camera.world2camera_pytorch(joint_cam_flat) |
|
|
| |
| joint_proj = joint_proj_flat.view(B, J, 2) |
| |
| |
| root_cam = joint_cam[:, smpl_x.root_joint_idx, None, :] |
| joint_cam = joint_cam - root_cam |
| |
| mesh_cam = mesh_cam + cam_trans[:, None, :] |
| joint_cam_wo_ra = joint_cam.clone() |
| |
| |
| joint_proj[..., 0] = joint_proj[..., 0] / cfg.input_img_shape[1] * cfg.output_hm_shape[2] |
| joint_proj[..., 1] = joint_proj[..., 1] / cfg.input_img_shape[0] * cfg.output_hm_shape[1] |
| |
| |
| |
| lhand_idx = smpl_x.joint_part['lhand'] |
| lhand_cam = joint_cam[:, lhand_idx, :] |
| lwrist_cam = joint_cam[:, smpl_x.lwrist_idx, None, :] |
| lhand_cam = lhand_cam - lwrist_cam |
| joint_cam = torch.cat((joint_cam[:, :lhand_idx[0], :], lhand_cam, joint_cam[:, lhand_idx[-1] + 1:, :]), 1) |
|
|
| |
| rhand_idx = smpl_x.joint_part['rhand'] |
| rhand_cam = joint_cam[:, rhand_idx, :] |
| rwrist_cam = joint_cam[:, smpl_x.rwrist_idx, None, :] |
| rhand_cam = rhand_cam - rwrist_cam |
| joint_cam = torch.cat((joint_cam[:, :rhand_idx[0], :], rhand_cam, joint_cam[:, rhand_idx[-1] + 1:, :]), 1) |
|
|
| |
| face_idx = smpl_x.joint_part['face'] |
| face_cam = joint_cam[:, face_idx, :] |
| neck_cam = joint_cam[:, smpl_x.neck_idx, None, :] |
| face_cam = face_cam - neck_cam |
| joint_cam = torch.cat((joint_cam[:, :face_idx[0], :], face_cam, joint_cam[:, face_idx[-1] + 1:, :]), 1) |
|
|
|
|
| return joint_proj, joint_cam, joint_cam_wo_ra, mesh_cam |
|
|
| def generate_mesh_gt(self, targets, mode): |
| if 'smplx_mesh_cam' in targets: |
| return targets['smplx_mesh_cam'] |
| nums = [3, 63, 45, 45, 3] |
| accu = [] |
| temp = 0 |
| for num in nums: |
| temp += num |
| accu.append(temp) |
| pose = targets['smplx_pose'] |
| root_pose, body_pose, lhand_pose, rhand_pose, jaw_pose = \ |
| pose[:, :accu[0]], pose[:, accu[0]:accu[1]], pose[:, accu[1]:accu[2]], pose[:, accu[2]:accu[3]], pose[:, |
| accu[3]: |
| accu[4]] |
| |
| shape = targets['smplx_shape'] |
| expr = targets['smplx_expr'] |
| cam_trans = targets['smplx_cam_trans'] |
|
|
| |
| joint_proj, joint_cam, joint_cam_wo_ra, mesh_cam = self.get_coord(root_pose, body_pose, lhand_pose, rhand_pose, jaw_pose, shape, |
| expr, cam_trans, mode) |
|
|
| return mesh_cam |
|
|
| def bbox_split(self, bbox): |
| |
| lhand_bbox_center, rhand_bbox_center, face_bbox_center = \ |
| bbox[:, 0, :2], bbox[:, 1, :2], bbox[:, 2, :2] |
| return lhand_bbox_center, rhand_bbox_center, face_bbox_center |
| |
| def build_forward_map(self,mapped_pixels): |
| """ |
| mapped_pixels: (H, W, 2) — dst(i,j) 위치에서 원본 좌표를 가져오는 역방향 맵 |
| 반환: KDTree와 보정 이미지 좌표 (dst_x, dst_y) |
| """ |
| H, W = mapped_pixels.shape[:2] |
| src_points = mapped_pixels.reshape(-1, 2) |
| dst_coords = np.stack(np.meshgrid(np.arange(W), np.arange(H)), axis=-1).reshape(-1, 2) |
|
|
| |
| valid_mask = np.all((src_points >= 0) & (src_points < np.array([W, H])), axis=1) |
| src_points = src_points[valid_mask] |
| dst_coords = dst_coords[valid_mask] |
|
|
| tree = cKDTree(src_points) |
| return tree, dst_coords |
|
|
| def remap_joints_forward(self, joint_orig, tree, dst_coords): |
| """ |
| joint_orig: (N, 2) — 원본 이미지 기준 joint |
| tree: KDTree of mapped_pixels (src → dst) |
| dst_coords: 보정된 이미지 좌표 |
| |
| 반환: 보정된 joint 위치 (N, 2) |
| """ |
| dist, idx = tree.query(joint_orig, k=1) |
| joint_remapped = dst_coords[idx] |
| return joint_remapped.astype(np.float32) |
| |
| |
| def draw_joint_lines(self, img_np, joints_2d, joint_names, chains, color=(0,255,0)): |
| """ |
| img_np: (H,W,3) numpy array (uint8) |
| joints_2d: (N,2) float (xy) |
| joint_names: 이름 리스트 |
| chains: 각 limb의 joint 인덱스 chain(순서대로 연결) |
| color: 선 색 |
| """ |
| |
| for x, y in joints_2d: |
| x_int, y_int = int(round(x)), int(round(y)) |
| if 0 <= x_int < img_np.shape[1] and 0 <= y_int < img_np.shape[0]: |
| cv2.circle(img_np, (x_int, y_int), 3, color, -1) |
|
|
| |
| for chain in chains: |
| for i in range(len(chain)-1): |
| idx1, idx2 = chain[i], chain[i+1] |
| x1, y1 = joints_2d[idx1] |
| x2, y2 = joints_2d[idx2] |
| pt1 = (int(round(x1)), int(round(y1))) |
| pt2 = (int(round(x2)), int(round(y2))) |
| if all(0 <= pt < img_np.shape[1] for pt in pt1) and all(0 <= pt < img_np.shape[0] for pt in pt1) and \ |
| all(0 <= pt < img_np.shape[1] for pt in pt2) and all(0 <= pt < img_np.shape[0] for pt in pt2): |
| cv2.line(img_np, pt1, pt2, (255, 128, 0), 2) |
|
|
| return img_np |
| |
| def visualize_mesh_on_image(self, img_np, mesh_cam, color=(0, 255, 0)): |
| """ |
| img_np: (H, W, 3) numpy array (uint8) |
| mesh_cam: (N, 3) torch.Tensor - 카메라 좌표계의 mesh vertices |
| """ |
| |
| mesh_cam_np = mesh_cam.detach().cpu().numpy() |
|
|
| |
| mesh_proj_2d = self.fisheye_camera.world2camera_pytorch( |
| torch.from_numpy(mesh_cam_np).float().cuda() |
| ).detach().cpu().numpy() |
|
|
| |
| for x, y in mesh_proj_2d: |
| x_int, y_int = int(round(x)), int(round(y)) |
| if 0 <= x_int < img_np.shape[1] and 0 <= y_int < img_np.shape[0]: |
| cv2.circle(img_np, (x_int, y_int), 1, color, -1) |
|
|
| return img_np |
|
|
| def forward(self, inputs, targets, meta_info, mode): |
| |
| |
| |
| |
|
|
| |
| |
| body_img = inputs['img_ori'] |
| |
| |
| |
| img_feat, task_tokens = self.encoder(body_img) |
| shape_token, cam_token, expr_token, jaw_pose_token, hand_token, body_pose_token = \ |
| task_tokens[:, 0], task_tokens[:, 1], task_tokens[:, 2], task_tokens[:, 3], task_tokens[:, 4:6], task_tokens[:, 6:] |
|
|
|
|
| |
|
|
| |
| |
| |
| |
| if not getattr(cfg, 'use_token_decoder', False): |
| body_joint_hm, body_joint_img = self.body_position_net(img_feat) |
| root_pose, body_pose, shape, cam_param, = self.body_regressor(body_pose_token, shape_token, cam_token, body_joint_img.detach()) |
| |
|
|
| else: |
| body_joint_hm, body_joint_img = self.body_position_net(img_feat) |
| |
| smpl_param = self.token_decoder(img_feat) |
| |
| root_pose = smpl_param['root_pose'] |
| body_pose = smpl_param['body_pose'] |
| shape = smpl_param['betas'] |
| cam_param = smpl_param['cam_param'] |
|
|
|
|
| |
| root_pose = rot6d_to_axis_angle(root_pose) |
| body_pose = rot6d_to_axis_angle(body_pose.reshape(-1, 6)).reshape(body_pose.shape[0], -1) |
| |
| |
| |
| lhand_bbox_center, lhand_bbox_size, rhand_bbox_center, rhand_bbox_size, face_bbox_center, face_bbox_size = self.box_net(img_feat, body_joint_hm.detach()) |
| lhand_bbox = restore_bbox(lhand_bbox_center, lhand_bbox_size, cfg.input_hand_shape[1] / cfg.input_hand_shape[0], 2.0).detach() |
| rhand_bbox = restore_bbox(rhand_bbox_center, rhand_bbox_size, cfg.input_hand_shape[1] / cfg.input_hand_shape[0], 2.0).detach() |
| face_bbox = restore_bbox(face_bbox_center, face_bbox_size, cfg.input_face_shape[1] / cfg.input_face_shape[0], 1.5).detach() |
|
|
| |
| |
| hand_feat = self.hand_roi_net(img_feat, lhand_bbox, rhand_bbox) |
|
|
| |
| |
| _, hand_joint_img = self.hand_position_net(hand_feat) |
| hand_pose = self.hand_regressor(hand_feat, hand_joint_img.detach()) |
| hand_pose = rot6d_to_axis_angle(hand_pose.reshape(-1, 6)).reshape(hand_feat.shape[0], -1) |
|
|
| |
| batch_size = hand_joint_img.shape[0] // 2 |
| lhand_joint_img = hand_joint_img[:batch_size, :, :] |
| lhand_joint_img = torch.cat((cfg.output_hand_hm_shape[2] - 1 - lhand_joint_img[:, :, 0:1], lhand_joint_img[:, :, 1:]), 2) |
| rhand_joint_img = hand_joint_img[batch_size:, :, :] |
| |
| batch_size = hand_pose.shape[0] // 2 |
| lhand_pose = hand_pose[:batch_size, :].reshape(-1, len(smpl_x.orig_joint_part['lhand']), 3) |
| lhand_pose = torch.cat((lhand_pose[:, :, 0:1], -lhand_pose[:, :, 1:3]), 2).view(batch_size, -1) |
| rhand_pose = hand_pose[batch_size:, :] |
|
|
| |
|
|
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
|
|
| |
| |
| |
| |
| |
|
|
| |
|
|
| |
| |
|
|
| |
| |
|
|
| |
| |
| |
|
|
|
|
| |
| expr, jaw_pose = self.face_regressor(expr_token, jaw_pose_token) |
| jaw_pose = rot6d_to_axis_angle(jaw_pose) |
|
|
| |
| joint_proj, joint_cam, joint_cam_wo_ra, mesh_cam = self.get_coord(root_pose, body_pose, lhand_pose, rhand_pose, jaw_pose, shape, expr, cam_param, mode) |
| |
| |
| pose = torch.cat((root_pose, body_pose, lhand_pose, rhand_pose, jaw_pose), 1) |
| |
|
|
|
|
| |
| B = body_pose.size(0) |
| device = body_pose.device |
| dtype = body_pose.dtype |
| expr_zeros = torch.zeros(B, 100, device=device, dtype=dtype) |
|
|
| pose_experssion = torch.cat((body_pose, lhand_pose, rhand_pose, jaw_pose, expr_zeros), 1) |
| joint_img = torch.cat((body_joint_img, lhand_joint_img, rhand_joint_img), 1) |
|
|
| |
| |
|
|
| |
|
|
| if mode == 'train': |
| loss = {} |
|
|
| if mode == 'train' and getattr(self, 'id_head', None) is not None: |
| id_loss_weight = getattr(cfg, 'id_loss_weight', 1.0) |
| |
| g = F.adaptive_avg_pool2d(img_feat, 1).flatten(1) |
|
|
|
|
| |
| |
|
|
| |
| id_logits = self.id_head(g) |
|
|
| |
| id_idx = meta_info['id_idx'].long() |
| id_target = F.one_hot(id_idx, num_classes=self.num_subjects).float() |
| loss['id_adv'] = self.id_loss_fn(id_logits, id_target) * id_loss_weight |
|
|
| |
| |
|
|
| smplx_kps_3d_weight = getattr(cfg, 'smplx_kps_3d_weight', 1.0) |
| smplx_kps_3d_weight = getattr(cfg, 'smplx_kps_weight', smplx_kps_3d_weight) |
|
|
| smplx_kps_2d_weight = getattr(cfg, 'smplx_kps_2d_weight', 1.0) |
| net_kps_2d_weight = getattr(cfg, 'net_kps_2d_weight', 1.0) |
|
|
| smplx_pose_weight = getattr(cfg, 'smplx_pose_weight', 1.0) |
| smplx_shape_weight = getattr(cfg, 'smplx_loss_weight', 1.0) |
|
|
| dposer_x_weight = getattr(cfg, 'dposer_x_weight', 1.0) |
| |
| |
|
|
| |
|
|
|
|
| |
| if getattr(cfg, 'agora_fix_global_orient_transl', False): |
| |
| if hasattr(cfg, 'smplx_orient_weight'): |
| smplx_orient_weight = getattr(cfg, 'smplx_orient_weight') |
| loss['smplx_orient'] = self.param_loss(pose, targets['smplx_pose'], meta_info['smplx_pose_valid'])[:, :3] * smplx_orient_weight |
|
|
| loss['smplx_pose'] = self.param_loss(pose, targets['smplx_pose'], meta_info['smplx_pose_valid']) * smplx_pose_weight |
| |
| |
|
|
| else: |
| loss['smplx_pose'] = self.param_loss(pose, targets['smplx_pose'], meta_info['smplx_pose_valid']) * smplx_pose_weight |
| |
| |
|
|
|
|
| loss['smplx_shape'] = self.param_loss(shape, targets['smplx_shape'], |
| meta_info['smplx_shape_valid'][:, None]) * smplx_shape_weight |
| loss['smplx_expr'] = self.param_loss(expr, targets['smplx_expr'], meta_info['smplx_expr_valid'][:, None]) |
|
|
| loss['dposerx'] = self.dposer_x(pose_experssion) * dposer_x_weight |
| |
| |
| |
|
|
|
|
| loss['smplx_joint_cam'] = self.coord_loss(joint_cam, targets['smplx_joint_cam'], meta_info['smplx_joint_valid']) * smplx_kps_3d_weight |
|
|
|
|
| |
| |
| |
| |
| |
| |
| if not (meta_info['lhand_bbox_valid'] == 0).all(): |
| loss['lhand_bbox'] = (self.coord_loss(lhand_bbox_center, targets['lhand_bbox_center'], meta_info['lhand_bbox_valid'][:, None]) + |
| self.coord_loss(lhand_bbox_size, targets['lhand_bbox_size'], meta_info['lhand_bbox_valid'][:, None])) |
| if not (meta_info['rhand_bbox_valid'] == 0).all(): |
| loss['rhand_bbox'] = (self.coord_loss(rhand_bbox_center, targets['rhand_bbox_center'], meta_info['rhand_bbox_valid'][:, None]) + |
| self.coord_loss(rhand_bbox_size, targets['rhand_bbox_size'], meta_info['rhand_bbox_valid'][:, None])) |
| if not (meta_info['face_bbox_valid'] == 0).all(): |
| loss['face_bbox'] = (self.coord_loss(face_bbox_center, targets['face_bbox_center'], meta_info['face_bbox_valid'][:, None]) + |
| self.coord_loss(face_bbox_size, targets['face_bbox_size'], meta_info['face_bbox_valid'][:, None])) |
| |
| |
| |
| targets['original_joint_img'] = targets['joint_img'].clone() |
| targets['original_smplx_joint_img'] = targets['smplx_joint_img'].clone() |
| |
| if not (meta_info['lhand_bbox_valid'] + meta_info['rhand_bbox_valid'] == 0).all(): |
|
|
| |
| for part_name, bbox in (('lhand', lhand_bbox), ('rhand', rhand_bbox)): |
| for coord_name, trunc_name in (('joint_img', 'joint_trunc'), ('smplx_joint_img', 'smplx_joint_trunc')): |
| x = targets[coord_name][:, smpl_x.joint_part[part_name], 0] |
| y = targets[coord_name][:, smpl_x.joint_part[part_name], 1] |
| z = targets[coord_name][:, smpl_x.joint_part[part_name], 2] |
| trunc = meta_info[trunc_name][:, smpl_x.joint_part[part_name], 0] |
|
|
| x -= (bbox[:, None, 0] / cfg.input_body_shape[1] * cfg.output_hm_shape[2]) |
| x *= (cfg.output_hand_hm_shape[2] / ( |
| (bbox[:, None, 2] - bbox[:, None, 0]) / cfg.input_body_shape[1] * cfg.output_hm_shape[ |
| 2])) |
| y -= (bbox[:, None, 1] / cfg.input_body_shape[0] * cfg.output_hm_shape[1]) |
| y *= (cfg.output_hand_hm_shape[1] / ( |
| (bbox[:, None, 3] - bbox[:, None, 1]) / cfg.input_body_shape[0] * cfg.output_hm_shape[ |
| 1])) |
| z *= cfg.output_hand_hm_shape[0] / cfg.output_hm_shape[0] |
| trunc *= ((x >= 0) * (x < cfg.output_hand_hm_shape[2]) * (y >= 0) * ( |
| y < cfg.output_hand_hm_shape[1])) |
|
|
| coord = torch.stack((x, y, z), 2) |
| trunc = trunc[:, :, None] |
| targets[coord_name] = torch.cat((targets[coord_name][:, :smpl_x.joint_part[part_name][0], :], coord, |
| targets[coord_name][:, smpl_x.joint_part[part_name][-1] + 1:, :]), |
| 1) |
| meta_info[trunc_name] = torch.cat((meta_info[trunc_name][:, :smpl_x.joint_part[part_name][0], :], |
| trunc, |
| meta_info[trunc_name][:, smpl_x.joint_part[part_name][-1] + 1:, |
| :]), 1) |
|
|
| |
| for part_name, bbox in (('lhand', lhand_bbox), ('rhand', rhand_bbox)): |
| x = joint_proj[:, smpl_x.joint_part[part_name], 0] |
| y = joint_proj[:, smpl_x.joint_part[part_name], 1] |
|
|
| x -= (bbox[:, None, 0] / cfg.input_body_shape[1] * cfg.output_hm_shape[2]) |
| x *= (cfg.output_hand_hm_shape[2] / ( |
| (bbox[:, None, 2] - bbox[:, None, 0]) / cfg.input_body_shape[1] * cfg.output_hm_shape[2])) |
| y -= (bbox[:, None, 1] / cfg.input_body_shape[0] * cfg.output_hm_shape[1]) |
| y *= (cfg.output_hand_hm_shape[1] / ( |
| (bbox[:, None, 3] - bbox[:, None, 1]) / cfg.input_body_shape[0] * cfg.output_hm_shape[1])) |
|
|
| coord = torch.stack((x, y), 2) |
| trans = [] |
| for bid in range(coord.shape[0]): |
| mask = meta_info['joint_trunc'][bid, smpl_x.joint_part[part_name], 0] == 1 |
| if torch.sum(mask) == 0: |
| trans.append(torch.zeros((2)).float().cuda()) |
| else: |
| trans.append((-coord[bid, mask, :2] + targets['joint_img'][:, smpl_x.joint_part[part_name], :][ |
| bid, mask, :2]).mean(0)) |
| trans = torch.stack(trans)[:, None, :] |
| coord = coord + trans |
| joint_proj = torch.cat((joint_proj[:, :smpl_x.joint_part[part_name][0], :], coord, |
| joint_proj[:, smpl_x.joint_part[part_name][-1] + 1:, :]), 1) |
|
|
| if not (meta_info['face_bbox_valid'] == 0).all(): |
| |
| coord = joint_proj[:, smpl_x.joint_part['face'], :] |
| trans = [] |
| for bid in range(coord.shape[0]): |
| mask = meta_info['joint_trunc'][bid, smpl_x.joint_part['face'], 0] == 1 |
| if torch.sum(mask) == 0: |
| trans.append(torch.zeros((2)).float().cuda()) |
| else: |
| trans.append((-coord[bid, mask, :2] + targets['joint_img'][:, smpl_x.joint_part['face'], :][bid, |
| mask, :2]).mean(0)) |
| trans = torch.stack(trans)[:, None, :] |
| coord = coord + trans |
| joint_proj = torch.cat((joint_proj[:, :smpl_x.joint_part['face'][0], :], coord, |
| joint_proj[:, smpl_x.joint_part['face'][-1] + 1:, :]), 1) |
| |
| |
| |
| |
|
|
|
|
| loss['joint_proj'] = self.coord_loss(joint_proj, targets['smplx_joint_img'][:, :, :2], meta_info['smplx_joint_trunc']) * smplx_kps_2d_weight |
| |
|
|
|
|
| |
| |
| |
|
|
| |
| loss['smplx_joint_img'] = self.coord_loss(joint_img, smpl_x.reduce_joint_set(targets['smplx_joint_img']), |
| smpl_x.reduce_joint_set(meta_info['smplx_joint_trunc'])) * net_kps_2d_weight |
|
|
| return loss |
| else: |
|
|
| save_dir = os.path.join(cfg.result_dir, 'vis_test_result') |
| os.makedirs(save_dir, exist_ok=True) |
| B = body_img.shape[0] |
|
|
| if getattr(cfg, 'vis_feature', False): |
| base_feat_dir = os.path.join(cfg.result_dir, 'vis_test_result', 'feature') |
| B = body_img.shape[0] |
|
|
| for i in range(B): |
| img_path_i = None |
| if 'img_path' in meta_info: |
| img_path_i = meta_info['img_path'][i] |
|
|
| |
| sub_rel = self._get_subject_scene_dir(img_path_i) if isinstance(img_path_i, str) else "" |
|
|
| if sub_rel: |
| feat_dir = os.path.join(base_feat_dir, sub_rel) |
| else: |
| feat_dir = base_feat_dir |
|
|
| os.makedirs(feat_dir, exist_ok=True) |
|
|
| if isinstance(img_path_i, str): |
| base = os.path.basename(img_path_i) |
| name = os.path.splitext(base)[0] |
| else: |
| name = f"{i:06d}" |
|
|
| save_path = os.path.join(feat_dir, f"{name}_feat.png") |
| |
| self._save_activation_heatmap(body_img[i], img_feat[i], save_path) |
|
|
|
|
| |
| for part_name, bbox in (('lhand', lhand_bbox), ('rhand', rhand_bbox)): |
| joint_img[:, smpl_x.pos_joint_part[part_name], 0] *= ( |
| ((bbox[:, None, 2] - bbox[:, None, 0]) / cfg.input_body_shape[1] * cfg.output_hm_shape[2]) / |
| cfg.output_hand_hm_shape[2]) |
| joint_img[:, smpl_x.pos_joint_part[part_name], 0] += ( |
| bbox[:, None, 0] / cfg.input_body_shape[1] * cfg.output_hm_shape[2]) |
| joint_img[:, smpl_x.pos_joint_part[part_name], 1] *= ( |
| ((bbox[:, None, 3] - bbox[:, None, 1]) / cfg.input_body_shape[0] * cfg.output_hm_shape[1]) / |
| cfg.output_hand_hm_shape[1]) |
| joint_img[:, smpl_x.pos_joint_part[part_name], 1] += ( |
| bbox[:, None, 1] / cfg.input_body_shape[0] * cfg.output_hm_shape[1]) |
|
|
| |
| for bbox in (lhand_bbox, rhand_bbox, face_bbox): |
| bbox[:, 0] *= cfg.input_img_shape[1] / cfg.input_body_shape[1] |
| bbox[:, 1] *= cfg.input_img_shape[0] / cfg.input_body_shape[0] |
| bbox[:, 2] *= cfg.input_img_shape[1] / cfg.input_body_shape[1] |
| bbox[:, 3] *= cfg.input_img_shape[0] / cfg.input_body_shape[0] |
|
|
|
|
| if getattr(cfg, 'vis_hand_bbox', False): |
| hand_bbox_dir = os.path.join(save_dir, 'hand_bbox') |
| os.makedirs(hand_bbox_dir, exist_ok=True) |
| self.visualize_hand_bboxes_on_input( |
| body_img, lhand_bbox, rhand_bbox, |
| hand_bbox_dir, meta_info |
| ) |
|
|
| |
| |
| |
| |
| |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
|
|
| |
| |
|
|
|
|
| |
| |
|
|
| |
| out = {} |
| out['img'] = inputs['img_ori'] |
| |
| out['joint_img'] = joint_img |
|
|
| out['smplx_joint_proj'] = joint_proj |
| out['smplx_mesh_cam'] = mesh_cam |
| out['smplx_root_pose'] = root_pose |
| out['smplx_body_pose'] = body_pose |
| out['smplx_lhand_pose'] = lhand_pose |
| out['smplx_rhand_pose'] = rhand_pose |
| out['smplx_jaw_pose'] = jaw_pose |
| out['smplx_shape'] = shape |
| out['smplx_expr'] = expr |
| |
| |
| |
| |
| if 'smplx_shape' in targets: |
| out['smplx_shape_target'] = targets['smplx_shape'] |
| if 'img_path' in meta_info: |
| out['img_path'] = meta_info['img_path'] |
| if 'joint_cam' in meta_info: |
| out['gt_joint'] = meta_info['joint_cam'] |
| |
| |
| if 'smplx_mesh_cam' in targets: |
| out['smplx_mesh_cam_target'] = targets['smplx_mesh_cam'] |
| if 'smpl_mesh_cam' in targets: |
| out['smpl_mesh_cam_target'] = targets['smpl_mesh_cam'] |
| if 'bb2img_trans' in meta_info: |
| out['bb2img_trans'] = meta_info['bb2img_trans'] |
| if 'gt_smplx_transl' in meta_info: |
| out['gt_smplx_transl'] = meta_info['gt_smplx_transl'] |
|
|
| return out |
|
|
| def init_weights(m): |
| try: |
| if type(m) == nn.ConvTranspose2d: |
| nn.init.normal_(m.weight, std=0.001) |
| elif type(m) == nn.Conv2d: |
| nn.init.normal_(m.weight, std=0.001) |
| nn.init.constant_(m.bias, 0) |
| elif type(m) == nn.BatchNorm2d: |
| nn.init.constant_(m.weight, 1) |
| nn.init.constant_(m.bias, 0) |
| elif type(m) == nn.Linear: |
| nn.init.normal_(m.weight, std=0.01) |
| nn.init.constant_(m.bias, 0) |
| except AttributeError: |
| pass |
|
|
| def reinit_cam_out(m): |
| """cam_out만 랜덤 초기화""" |
| if isinstance(m, nn.Linear): |
| nn.init.normal_(m.weight, std=0.01) |
| nn.init.constant_(m.bias, 0) |
|
|
|
|
| def get_model(mode): |
|
|
| |
| vit_cfg = Config.fromfile(cfg.encoder_config_file) |
| vit = build_posenet(vit_cfg.model) |
|
|
| token_decoder = None |
| if getattr(cfg, 'use_token_decoder', False): |
| token_cfg_path = cfg.token_cfg |
| token_cfg = OmegaConf.load(token_cfg_path) |
| token_decoder = SMPLTokenDecoderHead(token_cfg) |
|
|
| body_position_net = PositionNet('body', feat_dim=cfg.feat_dim) |
| body_rotation_net = BodyRotationNet(feat_dim=cfg.feat_dim) |
| box_net = BoxNet(feat_dim=cfg.feat_dim) |
|
|
| |
|
|
| |
| |
| |
|
|
| hand_position_net = PositionNet('hand', feat_dim=cfg.feat_dim) |
| hand_roi_net = HandRoI(feat_dim=cfg.feat_dim, upscale=cfg.upscale) |
| hand_rotation_net = HandRotationNet('hand', feat_dim=cfg.feat_dim) |
|
|
| |
| face_regressor = FaceRegressor(feat_dim=cfg.feat_dim) |
|
|
| if mode == 'train': |
| |
| if not getattr(cfg, 'random_init', False): |
| encoder_pretrained_model = torch.load(cfg.encoder_pretrained_model_path)['state_dict'] |
| vit.load_state_dict(encoder_pretrained_model, strict=False) |
| print(f"Initialize encoder from {cfg.encoder_pretrained_model_path}") |
| else: |
| print('Random init!!!!!!!') |
|
|
| if getattr(cfg, 'use_smpl', False): |
| body_position_net.apply(init_weights) |
| body_rotation_net.apply(init_weights) |
|
|
| else: |
|
|
| body_position_net.apply(init_weights) |
| body_rotation_net.apply(init_weights) |
| |
| box_net.apply(init_weights) |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| hand_position_net.apply(init_weights) |
| hand_roi_net.apply(init_weights) |
| hand_rotation_net.apply(init_weights) |
|
|
| |
| face_regressor.apply(init_weights) |
|
|
| |
|
|
| encoder = vit.backbone |
|
|
| model = Model(encoder, token_decoder, body_position_net, body_rotation_net, box_net, hand_position_net, hand_roi_net, hand_rotation_net, |
| face_regressor, mode) |
| |
| return model |
|
|