1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| import torch import torch.nn as nn
class PressureNet(nn.Module): """ PressureNet: 从压力图像预测 3D 人体姿态和体型 论文 Clever et al. (2020) 的简化实现 输入: 压力分布图 (B, 1, H, W) 输出: SMPL-X 参数 (pose: 72维, shape: 10维) """ def __init__(self, pressure_size=(72, 36)): super().__init__() self.encoder = nn.Sequential( nn.Conv2d(1, 32, 5, stride=2, padding=2), nn.BatchNorm2d(32), nn.ReLU(), nn.Conv2d(32, 64, 3, stride=2, padding=1), nn.BatchNorm2d(64), nn.ReLU(), nn.Conv2d(64, 128, 3, stride=2, padding=1), nn.BatchNorm2d(128), nn.ReLU(), nn.Conv2d(128, 256, 3, stride=2, padding=1), nn.BatchNorm2d(256), nn.ReLU(), ) h, w = pressure_size flat_h, flat_w = h // 16, w // 16 if flat_h == 0: flat_h = 1 if flat_w == 0: flat_w = 1 flat_dim = 256 * max(flat_h, 1) * max(flat_w, 1) self.fc = nn.Sequential( nn.Linear(flat_dim, 512), nn.ReLU(), nn.Dropout(0.3), nn.Linear(512, 256), nn.ReLU(), nn.Dropout(0.3), ) self.pose_head = nn.Linear(256, 72) self.shape_head = nn.Linear(256, 10) self.trans_head = nn.Linear(256, 3) def forward(self, pressure_img): """ Args: pressure_img: (B, 1, H, W) 归一化压力图 Returns: dict: pose, shape, translation """ feat = self.encoder(pressure_img) feat = feat.flatten(1) feat = self.fc(feat) pose = self.pose_head(feat) shape = self.shape_head(feat) trans = self.trans_head(feat) return { 'pose': pose, 'shape': shape, 'trans': trans, }
class PressureSynthesizer: """ 合成压力图像生成器 论文核心:用物理仿真生成训练数据 """ def __init__(self, grid_size=(72, 36)): self.grid_h, self.grid_w = grid_size self.cell_size = 0.027 def generate(self, pose_params, shape_params, body_mass=70): """ 从 SMPL 参数生成合成压力图 Args: pose_params: 24x3 关节角度 shape_params: 10 体型参数 body_mass: 体重 (kg) Returns: pressure_map: (H, W) 压力分布 """ pressure_map = np.zeros((self.grid_h, self.grid_w)) contact_regions = self._get_contact_regions(pose_params) for region in contact_regions: cx, cy, intensity = region for i in range(self.grid_h): for j in range(self.grid_w): dist = np.sqrt((i - cx)**2 + (j - cy)**2) pressure_map[i, j] += intensity * np.exp(-dist**2 / 4) pressure_map = pressure_map / (pressure_map.max() + 1e-6) return pressure_map def _get_contact_regions(self, pose_params): """根据姿态参数确定接触区域""" return [ (10, 18, 0.3), (30, 18, 0.5), (45, 18, 0.8), (60, 18, 0.4), (65, 18, 0.3), ]
import numpy as np
if __name__ == "__main__": synth = PressureSynthesizer() pose = np.zeros((24, 3)) shape = np.zeros(10) pressure_img = synth.generate(pose, shape) print(f"压力图尺寸: {pressure_img.shape}") print(f"压力值范围: {pressure_img.min():.3f} - {pressure_img.max():.3f}") print(f"非零点数: {np.count_nonzero(pressure_img > 0.01)}") model = PressureNet() x = torch.FloatTensor(pressure_img).unsqueeze(0).unsqueeze(0) output = model(x) print(f"\nPressureNet 输出:") print(f" Pose: {output['pose'].shape}") print(f" Shape: {output['shape'].shape}") print(f" Trans: {output['trans'].shape}") print(f" 参数总数: {sum(p.numel() for p in model.parameters()):,}")
|