Spaces:
Runtime error
Runtime error
File size: 5,994 Bytes
c3ec853 | 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 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | import os
import torch
import torch.nn as nn
from .resnet import resnet18, resnet34, resnet50
from .unet import UNetEncoder, UNetDecoder
from .pointnet import create_pointnet_encoder
from .mlp import create_pointcloud_decoder
DATASET_META = {
'tip': {
'max_p': 512.0,
'crop_size': [56, 40],
'path': "/workspace/zyk/public_data/wzy_opt_dataset_w_feats"
},
'pressurepose': {
'max_p': 100.0,
'crop_size': [64, 27],
'path': "/workspace/zyk/public_data/pressurepose/synth"
},
'moyo': {
'max_p': 64.0,
'crop_size': [110, 37],
'path': "/workspace/zyk/public_data/moyo"
}
}
class SMPL2PressureCVAE(nn.Module):
"""
SMPL2Pressure cVAE model with dual branches.
Main Branch: Pressure map encoding and reconstruction.
Condition Branch: Point cloud encoding and reconstruction.
"""
def __init__(self, cfg):
super(SMPL2PressureCVAE, self).__init__()
self.cfg = cfg
self.embed_dim = cfg['model']['embed_dim']
self.cond_embed_dim = cfg['model']['cond_embed_dim']
# 1. Main Encoder (Pressure Map -> z_params)
# Supports ResNet or UNet as the visual encoder
main_enc_type = cfg['model']['main_encoder']['type']
if "resnet" in main_enc_type:
# Using the ResNet implementation provided
model_func = eval(main_enc_type)
self.main_encoder = model_func(
embed_dim=self.embed_dim,
cond_embed_dim=self.cond_embed_dim,
dp_rate=cfg['model']['dropout_rate']
)
else:
self.main_encoder = UNetEncoder(
cond_dim=self.cond_embed_dim,
embed_dim=self.embed_dim,
crop=DATASET_META[cfg['dataset']['name']]['crop_size'] # Need to pass this from config
)
# 2. Condition Encoder (Point Cloud -> cond_features)
self.cond_encoder = create_pointnet_encoder(
input_dim=3,
feature_dim=self.cond_embed_dim,
use_spatial_transformer=cfg['model']['cond_encoder']['use_spatial_transformer'],
return_global_feature=cfg['model']['cond_encoder']['return_global_feature']
)
# 3. Main Decoder (z + cond -> Pressure Map)
self.main_decoder = UNetDecoder(
cond_dim=self.cond_embed_dim,
embed_dim=self.embed_dim,
bilinear=cfg['model']['main_decoder']['bilinear'],
crop=DATASET_META[cfg['dataset']['name']]['crop_size']
)
# 4. Condition Decoder (cond_features -> Point Cloud)
self.cond_decoder = create_pointcloud_decoder(
latent_dim=self.embed_dim,
num_points=6890, # SMPL vertices
architecture=cfg['model']['cond_decoder']['type']
)
def load_pretrained_cond(self, path):
"""
专门用于加载预训练的点云编解码分支权重
"""
if not os.path.exists(path):
print(f"Warning: Pretrained path {path} not found. Training from scratch.")
return
print(f"Loading pretrained condition branch from {path}...")
ckpt = torch.load(path, map_location='cpu')
# 加载 encoder 和 decoder
self.cond_encoder.load_state_dict(ckpt['cond_encoder'])
self.cond_decoder.load_state_dict(ckpt['cond_decoder'])
# 可选:如果你希望预训练的组件在主训练初期不被破坏,可以冻结它们
# for param in self.cond_encoder.parameters(): param.requires_grad = False
def reparameterize(self, mu, log_var):
"""Reparameterization trick to sample z from N(mu, var)"""
std = torch.exp(0.5 * log_var)
eps = torch.randn_like(std)
return mu + eps * std
def forward(self, pressure_map, vertices):
"""
Forward pass during training.
Args:
pressure_map: (B, 1, H, W)
vertices: (B, 6890, 3)
"""
# A. Encode Point Cloud to get condition (Condition branch)
# PointNet expects (B, 3, N)
pts = vertices.transpose(2, 1)
cond_feat = self.cond_encoder(pts) # (B, cond_embed_dim)
# B. Encode Pressure Map with condition to get latent distribution
# Note: resnet implementation already handles concatenation inside
mu, log_var = self.main_encoder(pressure_map, cond_feat)
# C. Sample z
z = self.reparameterize(mu, log_var)
# D. Reconstruct Pressure Map
recon_pressure = self.main_decoder(z, cond_feat)
# E. Reconstruct Point Cloud (Side task for better latent space)
recon_vertices = self.cond_decoder(cond_feat)
return {
'recon_pressure': recon_pressure,
'recon_vertices': recon_vertices,
'mu': mu,
'log_var': log_var,
'z': z
}
@torch.no_grad()
def inference(self, vertices):
"""
Inference: Generate pressure from Point Cloud only.
"""
self.eval()
# 1. Encode Point Cloud
pts = vertices.transpose(2, 1)
cond_feat = self.cond_encoder(pts)
# 2. Sample z from prior N(0, 1)
z = torch.randn(vertices.size(0), self.embed_dim).to(vertices.device)
# 3. Decode to Pressure Map
gen_pressure = self.main_decoder(z, cond_feat)
return gen_pressure
if __name__ == "__main__":
import yaml
cfg_path = 'config/config_base.yaml'
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
with open(cfg_path, 'r') as f:
cfg = yaml.safe_load(f)
model = SMPL2PressureCVAE(cfg).to(device)
pressure = torch.randn(8, 1, 56, 40).to(device)
vertices = torch.randn(8, 6890, 3).to(device)
res = model(pressure, vertices)
pred_pressure = model.inference(vertices)
import pdb; pdb.set_trace()
|