4 Commits
Author SHA1 Message Date
luni bfda596f8f add cvae training 2023-05-05 14:35:17 +00:00
luni 89d887e220 dataset fix 2023-05-05 14:01:04 +00:00
luni d63c778ccd implement postprocessing in torch 2023-05-05 14:00:46 +00:00
luni a20b4b1d50 fix smpl when running on garments 2023-05-05 14:00:24 +00:00
10 changed files with 458 additions and 513 deletions
+194
View File
@@ -0,0 +1,194 @@
from utils.convnext_utils import CNBlockConfig
config = dict()
config['gpu'] = '0'
config['name'] = 'abl_torch_all'
config['notes'] = ''
config['tags'] = ['iccv', 'abl_all', 'torch']
config['script'] = 'scripts/cvae.py'
config['use_skinning'] = True # and deshaping
config['remove_global_orientation'] = True
config['use_curvature'] = True
config['use_extra_losses'] = True # edge, collision, 3d mesh
config['use_offsets'] = True
config['use_norm_loss'] = True
ablation_count = [config['use_skinning'], config['remove_global_orientation'], config['use_curvature'], config['use_extra_losses'], config['use_offsets']].count(False)
assert ablation_count == 1 or ablation_count == 0
config['avg_garment_path'] = 'custom_data/avg/train_no_shape_avg_uv.npy' #custom_data/train_no_shape_avg_uv.npy # train_no_shape_avg_uv train_no_shape_one_hot_avg_uv
if config['use_skinning']:
assert (config['use_offsets'] and config['avg_garment_path'] != '') or (not config['use_offsets'] and config['avg_garment_path'] == '')
config['reg_loss_coeff'] = 0.1 # set to 0 if don't want to use edge+collision
config['batch_size'] = 4
config['resolution'] = 512
config['is_skirt'] = False
config['dataset_in_memory'] = False
config['is_cape'] = False
config['device'] = 'cuda'
config['train_dir'] = '/data-local/127/train'
config['valid_dir'] = '/data-local/127/valid'
config['test_dir'] = '/data-net/dataset/test_ready'
config['train_frames'] = None
config['valid_frames'] = None
config['static_model'] = 'checkpoints/iccv/static_torch_orig_2'
config['body_model'] = 'checkpoints/iccv/body_torch_1'
config['curvature_model'] = 'checkpoints/iccv/curv_torch_1'
config['smpl_res'] = 14475
if config['is_cape']:
config['smpl_res'] = 3638
config['latent_dim'] = 128
config['pretrained_model'] = ''
###############
config['lr'] = 0.0001# 0.0001
###############
config['starting_balancing_coeff'] = 0.0001
config['signature']: set = { 'id',
'frame_nr',
'pose_shape',
'uv_body_posed',
'mesh_body_posed',
'mesh_posed',
'uv_curvature',
'uv_static',
'mesh_mask',
'uv_mask',
}
if config['use_skinning']:
config['signature'].add('uv_unposed')
config['signature'].add('mesh_unposed')
else:
config['signature'].add('uv_posed')
config['upscale_enhancer'] = ''
config['freeze_encoders'] = True
config['use_one_hot'] = False
config['bottleneck_scale'] = 2
config['cvae'] = dict()
config['cvae']['device'] = config['device']
config['cvae']['is_label'] = False
config['cvae']['input_features'] = 3+3+3+7 if config['use_curvature'] else 3+3+3
config['cvae']['kernel_size'] = 7
config['cvae']['bottleneck_size'] = 64 * config['bottleneck_scale']
config['cvae']['latent_dim'] = config['cvae']['bottleneck_size']
block_setting = [
CNBlockConfig(32, 64, 3),
CNBlockConfig(64, 128, 3),
CNBlockConfig(128, 256, 3),
CNBlockConfig(256, config['cvae']['bottleneck_size'], 9),
CNBlockConfig(config['cvae']['bottleneck_size'], None, 3),
]
block_setting_reverse = [
CNBlockConfig(config['cvae']['bottleneck_size'] * 2, 256, 3),
CNBlockConfig(256, 128, 9),
CNBlockConfig(128, 64, 9),
CNBlockConfig(64, 32, 3),
CNBlockConfig(32, 3, 3),
]
config['cvae']['encoder_block_settings'] = block_setting
config['cvae']['decoder_block_settings'] = block_setting_reverse
####################### cond_ae_curv ############################
config['cond_ae_curv'] = dict()
config['cond_ae_curv']['device'] = config['device']
config['cond_ae_curv']['is_label'] = True
config['cond_ae_curv']['latent_dim'] = config['latent_dim']
config['cond_ae_curv']['kernel_size'] = 7
config['cond_ae_curv']['bottleneck_size'] = 40 * config['bottleneck_scale']
block_setting = [
CNBlockConfig(16, 32, 3),
CNBlockConfig(32, 64, 3),
CNBlockConfig(64, 128, 9),
CNBlockConfig(128, config['cond_ae_curv']['bottleneck_size'], 9),
CNBlockConfig(config['cond_ae_curv']['bottleneck_size'], None, 3),
]
block_setting_reverse = [
CNBlockConfig(config['cond_ae_curv']['bottleneck_size'], 128, 3),
CNBlockConfig(128, 64, 9),
CNBlockConfig(64, 32, 9),
CNBlockConfig(32, 16, 3),
CNBlockConfig(16, 3, 3),
]
config['cond_ae_curv']['encoder_block_settings'] = block_setting
config['cond_ae_curv']['decoder_block_settings'] = block_setting_reverse
config['cond_ae_curv']['activation'] = 'tanh'
####################### cond_ae_body ############################
config['cond_ae_body'] = dict()
config['cond_ae_body']['device'] = config['device']
config['cond_ae_body']['is_label'] = False
config['cond_ae_body']['latent_dim'] = config['latent_dim']
config['cond_ae_body']['kernel_size'] = 7
config['cond_ae_body']['bottleneck_size'] = 16 * config['bottleneck_scale']
block_setting = [
CNBlockConfig(16, 32, 3),
CNBlockConfig(32, 64, 3),
CNBlockConfig(64, 128, 9),
CNBlockConfig(128, config['cond_ae_body']['bottleneck_size'], 9),
CNBlockConfig(config['cond_ae_body']['bottleneck_size'], None, 3),
]
block_setting_reverse = [
CNBlockConfig(config['cond_ae_body']['bottleneck_size'], 128, 3),
CNBlockConfig(128, 64, 9),
CNBlockConfig(64, 32, 9),
CNBlockConfig(32, 16, 3),
CNBlockConfig(16, 3, 3),
]
config['cond_ae_body']['encoder_block_settings'] = block_setting
config['cond_ae_body']['decoder_block_settings'] = block_setting_reverse
config['cond_ae_body']['activation'] = 'tanh'
####################### cond_ae_static ############################
config['cond_ae_static'] = dict()
config['cond_ae_static']['device'] = config['device']
config['cond_ae_static']['is_label'] = False
config['cond_ae_static']['latent_dim'] = config['latent_dim']
config['cond_ae_static']['kernel_size'] = 7
config['cond_ae_static']['bottleneck_size'] = 8 * config['bottleneck_scale']
block_setting = [
CNBlockConfig(8, 16, 3),
CNBlockConfig(16, 32, 3),
CNBlockConfig(32, 64, 3),
CNBlockConfig(64, config['cond_ae_static']['bottleneck_size'], 9),
CNBlockConfig(config['cond_ae_static']['bottleneck_size'], None, 3),
]
block_setting_reverse = [
CNBlockConfig(config['cond_ae_static']['bottleneck_size'], 64, 3),
CNBlockConfig(64, 32, 9),
CNBlockConfig(32, 16, 3),
CNBlockConfig(16, 8, 3),
CNBlockConfig(8, 3, 3),
]
config['cond_ae_static']['encoder_block_settings'] = block_setting
config['cond_ae_static']['decoder_block_settings'] = block_setting_reverse
config['cond_ae_static']['activation'] = 'tanh'
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -159,7 +159,7 @@ class Dataset(Dataset):
if 'frame_nr' in self.signature: if 'frame_nr' in self.signature:
item['frame_nr'] = frame.frame_nr item['frame_nr'] = frame.frame_nr
if 'pose_shape' in self.signature: if 'pose_shape' in self.signature:
pose_shape = np.load(os.path.join(self.folder, 'pose_shape', frame.id + separator + frame.frame_nr + '.npy')) pose_shape = np.load(os.path.join(self.folder, 'pose_shape', frame.id + separator + frame.frame_nr + '.npy')).astype(np.float32)
pose_shape[:3] = 0 pose_shape[:3] = 0
item['pose_shape'] = pose_shape item['pose_shape'] = pose_shape
if 'mesh_body_posed' in self.signature: if 'mesh_body_posed' in self.signature:
+8 -6
View File
@@ -6,22 +6,24 @@ from typing import Any, Callable, List, Optional, Sequence
class VariationalEncoder(nn.Module): class VariationalEncoder(nn.Module):
def __init__(self, block_setting: CNBlockConfig, kernel_size: int = 7): def __init__(self, block_setting: List[CNBlockConfig], kernel_size: int = 7, input_features: int = 16, device: str = 'cuda'):
super(VariationalEncoder, self).__init__() super(VariationalEncoder, self).__init__()
self.encoder = ConvNeXt(block_setting=block_setting, kernel_size=kernel_size) self.encoder = ConvNeXt(block_setting=block_setting, kernel_size=kernel_size, input_features=input_features, device=device)
self.mean_branch = nn.Sequential([CNBlock(block_setting[-1].out_channels, kernel_size=kernel_size) for _ in range(5)]) layers = [CNBlock(block_setting[-1].input_channels, kernel_size=kernel_size) for _ in range(5)]
self.var_branch = nn.Sequential([CNBlock(block_setting[-1].out_channels, kernel_size=kernel_size) for _ in range(5)]) self.mean_branch = nn.Sequential(*layers).to(device)
layers = [CNBlock(block_setting[-1].input_channels, kernel_size=kernel_size) for _ in range(5)]
self.var_branch = nn.Sequential(*layers).to(device)
self.flatten_mean = nn.Flatten() self.flatten_mean = nn.Flatten()
self.flatten_var = nn.Flatten() self.flatten_var = nn.Flatten()
def call(self, inputs, training): def forward(self, inputs):
x = inputs x = inputs
x = self.encoder(x, training=training) x = self.encoder(x)
mean = self.mean_branch(x) mean = self.mean_branch(x)
log_var = self.var_branch(x) log_var = self.var_branch(x)
+6 -4
View File
@@ -66,6 +66,7 @@ class ConvNeXtBase(nn.Module):
kernel_size: int = 7, kernel_size: int = 7,
is_label: bool = False, is_label: bool = False,
nr_of_labels: Optional[int] = None, nr_of_labels: Optional[int] = None,
input_features: int = 3,
) -> None: ) -> None:
super().__init__() super().__init__()
@@ -85,7 +86,7 @@ class ConvNeXtBase(nn.Module):
# Stem # Stem
firstconv_output_channels = block_setting[0].input_channels firstconv_output_channels = block_setting[0].input_channels
self.stem = Conv2dNormActivation( self.stem = Conv2dNormActivation(
3 if not is_label else nr_of_labels, input_features if not is_label else nr_of_labels,
firstconv_output_channels, firstconv_output_channels,
kernel_size=4, kernel_size=4,
stride=4, stride=4,
@@ -229,6 +230,7 @@ def _convnext(
is_label: bool, is_label: bool,
nr_of_labels: Optional[int], nr_of_labels: Optional[int],
kernel_size: int, kernel_size: int,
input_features: int,
device='cuda', device='cuda',
**kwargs: Any, **kwargs: Any,
) -> ConvNeXtBase: ) -> ConvNeXtBase:
@@ -236,17 +238,17 @@ def _convnext(
if reversed: if reversed:
model = ConvNeXtReverseBase(block_setting, stochastic_depth_prob=stochastic_depth_prob, is_label=is_label, nr_of_labels=nr_of_labels, kernel_size=kernel_size, device=device, **kwargs) model = ConvNeXtReverseBase(block_setting, stochastic_depth_prob=stochastic_depth_prob, is_label=is_label, nr_of_labels=nr_of_labels, kernel_size=kernel_size, device=device, **kwargs)
else: else:
model = ConvNeXtBase(block_setting, stochastic_depth_prob=stochastic_depth_prob, kernel_size=kernel_size, is_label=is_label, nr_of_labels=nr_of_labels, device=device, **kwargs) model = ConvNeXtBase(block_setting, stochastic_depth_prob=stochastic_depth_prob, kernel_size=kernel_size, is_label=is_label, nr_of_labels=nr_of_labels, input_features=input_features, device=device, **kwargs)
return model return model
class ConvNeXt(nn.Module): class ConvNeXt(nn.Module):
def __init__(self, block_setting, stochastic_depth_prob=0.0, reversed=False, is_label=False, nr_of_labels=None, kernel_size=7, device='cuda', **kwargs) -> None: def __init__(self, block_setting, stochastic_depth_prob=0.0, reversed=False, is_label=False, nr_of_labels=None, kernel_size=7, input_features=3, device='cuda', **kwargs) -> None:
super().__init__() super().__init__()
self.model = _convnext(block_setting, stochastic_depth_prob, reversed, is_label, nr_of_labels, kernel_size, device=device, **kwargs) self.model: nn.Module = _convnext(block_setting, stochastic_depth_prob, reversed, is_label, nr_of_labels, kernel_size, input_features=input_features, device=device, **kwargs)
def forward(self, x: Tensor) -> Tensor: def forward(self, x: Tensor) -> Tensor:
x = self.model(x) x = self.model(x)
+37 -192
View File
@@ -1,63 +1,54 @@
import os import os
import time import time
import random import random
import tensorflow as tf import torch
from torch import nn
import numpy as np import numpy as np
import scipy as sp import scipy as sp
# import plotly.graph_objects as go # import plotly.graph_objects as go
# import plotly.express as ex # import plotly.express as ex
from custom_utils.custom_utils import load_model from utils.checkpoints import load_model
from models.base import AEEncoder, VariationalEncoder, Decoder from models.base import AEEncoder, VariationalEncoder, Decoder
from models.base import AEEncoderNew, VariationalEncoderNew, DecoderNew
class CVAE(tf.keras.Model): class CVAE(nn.Module):
def __init__(self, config): def __init__(self, config):
super(CVAE, self).__init__() super(CVAE, self).__init__()
self.latent_dim = config['cvae']['latent_dim']
self.size = config['resolution'] self.size = config['resolution']
if not('only_curvatures' in config.keys() and config['only_curvatures'] == True): if not('only_curvatures' in config.keys() and config['only_curvatures'] == True):
self.static_enc : AEEncoder = AEEncoder(config['cond_ae']['latent_dim'], config['cond_ae']['encoder_depths'], config['cond_ae']['encoder_dims'], config['cond_ae']['bottleneck_size'], config['cond_ae']['kernel_size']) self.static_enc : AEEncoder = AEEncoder(config['cond_ae_static']['encoder_block_settings'], config['cond_ae_static']['kernel_size'], config['cond_ae_static']['activation'], device=config['device'])
if config['static_model'] != '': if config['static_model'] != '':
load_model(self.static_enc, config['static_model'] + '_enc') load_model(self.static_enc, config['static_model'] + '_enc')
self.body_enc : AEEncoder = AEEncoder(config['cond_ae_body']['latent_dim'], config['cond_ae_body']['encoder_depths'], config['cond_ae_body']['encoder_dims'], config['cond_ae_body']['bottleneck_size'], config['cond_ae_body']['kernel_size']) self.body_enc : AEEncoder = AEEncoder(config['cond_ae_body']['encoder_block_settings'], config['cond_ae_body']['kernel_size'], config['cond_ae_body']['activation'], device=config['device'])
if config['body_model'] != '': if config['body_model'] != '':
load_model(self.body_enc, config['body_model'] + '_enc') load_model(self.body_enc, config['body_model'] + '_enc')
if config['use_curvature']: if config['use_curvature']:
self.curv_enc : AEEncoder = AEEncoder(config['cond_ae']['latent_dim'], config['cond_ae']['encoder_depths'], config['cond_ae']['encoder_dims'], config['cond_ae']['bottleneck_size'], config['cond_ae']['kernel_size']) self.curv_enc : AEEncoder = AEEncoder(config['cond_ae_curv']['encoder_block_settings'], config['cond_ae_curv']['kernel_size'], config['cond_ae_curv']['activation'], device=config['device'], is_label=True, nr_of_labels=7)
if config['curvature_model'] != '': if config['curvature_model'] != '':
load_model(self.curv_enc, config['curvature_model'] + '_enc') load_model(self.curv_enc, config['curvature_model'] + '_enc')
if config['freeze_encoders']: if config['freeze_encoders']:
if not('only_curvatures' in config.keys() and config['only_curvatures'] == True): for param in self.static_enc.parameters(): param.requires_grad = False
self.static_enc.trainable = False for param in self.body_enc.parameters(): param.requires_grad = False
self.body_enc.trainable = False
if config['use_curvature']: if config['use_curvature']:
self.curv_enc.trainable = False for param in self.curv_enc.parameters(): param.requires_grad = False
self.cond_encoder = tf.keras.Sequential([tf.keras.layers.Dense(512, activation='relu'), self.encoder = VariationalEncoder(config['cvae']['encoder_block_settings'], config['cvae']['kernel_size'], config['cvae']['input_features'], device=config['device'])
tf.keras.layers.Dense(256, activation='relu'), self.decoder : Decoder = Decoder(config['cvae']['decoder_block_settings'], kernel_size=config['cvae']['kernel_size'], device=config['device'])
tf.keras.layers.Dense(128, activation='relu'),
# tf.keras.layers.Lambda(lambda x: x / 1000),
tf.keras.layers.Dense(config['latent_dim'], activation='tanh')])
self.encoder = VariationalEncoder(config['cvae']['latent_dim'], config['cvae']['encoder_depths'], config['cvae']['encoder_dims'], kernel_size=config['cvae']['kernel_size']) self.latent_shape = (config['cvae']['latent_dim'], 8, 4)
self.decoder = Decoder(config['cvae']['decoder_input'], config['cvae']['decoder_depths'], config['cvae']['decoder_dims'], config['cvae']['kernel_size'])
if config['avg_garment_path'] != '': if config['avg_garment_path'] != '':
self.avg_garment = np.load(config['avg_garment_path']) self.avg_garment = torch.permute(torch.tensor(np.load(config['avg_garment_path']), device=config['device']), [2,0,1])
else: else:
self.avg_garment = None self.avg_garment = None
self.add_noise = config['cvae']['add_noise']
self.noise_amount = config['cvae']['noise_amount']
self.use_curvature = config['use_curvature'] self.use_curvature = config['use_curvature']
self.only_curvatures = config['only_curvatures'] if 'only_curvatures' in config.keys() and config['only_curvatures'] == True else None
self.use_offsets_over_body = not config['use_skinning'] self.use_offsets_over_body = not config['use_skinning']
if self.use_offsets_over_body: if self.use_offsets_over_body:
@@ -68,7 +59,7 @@ class CVAE(tf.keras.Model):
# @tf.function # @tf.function
def sample(self, uv_template, uv_body_posed, uv_curv, uv_mask, noise=None, add_noise=False): def sample(self, uv_template, uv_body_posed, uv_curv, uv_mask, noise=None, add_noise=False):
if noise is None: if noise is None:
eps = tf.random.normal(shape=(uv_template.shape[0], self.latent_dim)) eps = tf.random.normal(shape=(uv_template.shape[0], 8, 4, self.latent_dim))
else: else:
eps = noise eps = noise
@@ -91,195 +82,49 @@ class CVAE(tf.keras.Model):
return x return x
def reparameterize(self, mean, logvar, training, fix_noise=False): def reparameterize(self, mean: torch.Tensor, logvar: torch.Tensor, training: bool = True, fix_noise: bool = False):
if training == True: if training == True:
if not fix_noise: if not fix_noise:
eps = tf.random.normal(shape=mean.shape) eps = torch.randn(size=mean.shape).to(mean.device)
else: else:
eps = tf.random.normal(shape=(1, mean.shape[1])) eps = torch.randn(size=(1, mean.shape[1]))
eps = tf.tile(eps, [mean.shape[0],1]) eps = torch.tile(eps, [mean.shape[0],1])
out = eps * tf.exp(logvar * .5) + mean out = eps * torch.exp(logvar * 0.5) + mean
else: else:
out = tf.exp(logvar * .5) + mean out = torch.exp(logvar * 0.5) + mean
return out return out
def encode_conds(self, uv_template, uv_body_posed, uv_curv, training): def encode_conds(self, uv_template, uv_body_posed, uv_curv):
if self.only_curvatures: z_body = self.body_enc(uv_body_posed)
z_cond = self.curv_enc(uv_curv, training=training) z_static = self.static_enc(uv_template)
else:
z_body = self.body_enc(uv_body_posed, training=training)
z_static = self.static_enc(uv_template, training=training)
if self.use_curvature: if self.use_curvature:
z_curv = self.curv_enc(uv_curv, training=training) z_curv = self.curv_enc(uv_curv)
z_cond = tf.concat([z_static, z_body, z_curv], axis=-1) z_cond = torch.concat([z_static, z_body, z_curv], dim=1)
else: else:
z_cond = tf.concat([z_static, z_body], axis=-1) z_cond = torch.concat([z_static, z_body], dim=1)
z_cond = self.cond_encoder(z_cond, training=training)
return z_cond return z_cond
def call(self, inputs, training): def forward(self, inputs):
uv_unposed, uv_template, uv_body_posed, uv_curv, uv_mask = inputs uv_unposed, uv_template, uv_body_posed, uv_curv, uv_mask = inputs
if self.only_curvatures: x = torch.concat([uv_unposed, uv_template, uv_body_posed, uv_curv], dim=1)
x = tf.concat([uv_unposed, uv_curv], axis=-1)
else:
x = tf.concat([uv_unposed, uv_template, uv_body_posed, uv_curv], axis=-1)
mean, logvar = self.encoder(x, training=training) mean, logvar = self.encoder(x)
z = self.reparameterize(mean, logvar, training) z: torch.Tensor = self.reparameterize(mean, logvar)
z_cond = self.encode_conds(uv_template, uv_body_posed, uv_curv, training=training) z = z.view((mean.shape[0], *self.latent_shape))
z_cond = self.encode_conds(uv_template, uv_body_posed, uv_curv)
if self.add_noise and training == True: z = torch.concat([z, z_cond], dim=1)
z_cond = z_cond + (tf.random.uniform(z_cond.shape, maxval=self.noise_amount * 2) - self.noise_amount) x = self.decoder(z)
z = tf.concat([z, z_cond], axis=-1)
x = self.decoder(z, training=training)
if self.avg_garment is not None: if self.avg_garment is not None:
x = x + self.avg_garment[tf.newaxis,:,:,:] x = x + self.avg_garment[None,:,:,:]
if self.use_offsets_over_body: if self.use_offsets_over_body:
x = x + uv_body_posed x = x + uv_body_posed
x = x * uv_mask[:,:,:,tf.newaxis] x = x * uv_mask[:,None,:,:]
return x, mean, logvar return x, mean, logvar
class CVAENew(tf.keras.Model):
def __init__(self, config):
super(CVAENew, self).__init__()
self.size = config['resolution']
if not('only_curvatures' in config.keys() and config['only_curvatures'] == True):
self.static_enc : AEEncoderNew = AEEncoderNew(config['cond_ae_static']['encoder_depths'], config['cond_ae_static']['encoder_dims'], config['cond_ae_static']['kernel_size'], config['cond_ae_static']['activation'])
if config['static_model'] != '':
load_model(self.static_enc, config['static_model'] + '_enc')
self.body_enc : AEEncoderNew = AEEncoderNew(config['cond_ae_body']['encoder_depths'], config['cond_ae_body']['encoder_dims'], config['cond_ae_body']['kernel_size'])
if config['body_model'] != '':
load_model(self.body_enc, config['body_model'] + '_enc')
if config['use_curvature']:
self.curv_enc : AEEncoderNew = AEEncoderNew(config['cond_ae_curv']['encoder_depths'], config['cond_ae_curv']['encoder_dims'], config['cond_ae_curv']['kernel_size'], config['cond_ae_curv']['activation'])
if config['curvature_model'] != '':
load_model(self.curv_enc, config['curvature_model'] + '_enc')
if config['freeze_encoders']:
if not('only_curvatures' in config.keys() and config['only_curvatures'] == True):
self.static_enc.trainable = False
self.body_enc.trainable = False
if config['use_curvature']:
self.curv_enc.trainable = False
self.encoder = VariationalEncoderNew(config['cvae']['encoder_depths'], config['cvae']['encoder_dims'], kernel_size=config['cvae']['kernel_size'])
self.decoder = DecoderNew(config['cvae']['decoder_depths'], config['cvae']['decoder_dims'], config['cvae']['kernel_size'])
self.reshape = tf.keras.layers.Reshape((8, 4, 64))
if config['avg_garment_path'] != '':
self.avg_garment = np.load(config['avg_garment_path'])
else:
self.avg_garment = None
self.add_noise = config['cvae']['add_noise']
self.noise_amount = config['cvae']['noise_amount']
self.use_curvature = config['use_curvature']
self.only_curvatures = config['only_curvatures'] if 'only_curvatures' in config.keys() and config['only_curvatures'] == True else None
self.use_offsets_over_body = not config['use_skinning']
if self.use_offsets_over_body:
assert self.avg_garment is None, 'When we are not using skinning, we can\'t use avg_garment!'
if self.avg_garment is not None:
assert not self.use_offsets_over_body, 'When avg_garment is given, we must use skinning!'
# @tf.function
def sample(self, uv_template, uv_body_posed, uv_curv, uv_mask, noise=None, add_noise=False):
if noise is None:
eps = tf.random.normal(shape=(uv_template.shape[0], self.latent_dim))
else:
eps = noise
z_cond = self.encode_conds(uv_template, uv_body_posed, uv_curv, training=False)
if add_noise:
z_cond = z_cond + (tf.random.uniform(z_cond.shape, maxval=self.noise_amount * 2) - self.noise_amount)
z = tf.concat([eps, z_cond], axis=-1)
x = self.decoder(z, training=False)
if self.avg_garment is not None:
x = x + self.avg_garment[tf.newaxis,:,:,:]
if self.use_offsets_over_body:
x = x + uv_body_posed
x = x * uv_mask[:,:,:,tf.newaxis]
return x
def reparameterize(self, mean, logvar, training, fix_noise=False):
if training == True:
if not fix_noise:
eps = tf.random.normal(shape=mean.shape)
else:
eps = tf.random.normal(shape=(1, mean.shape[1]))
eps = tf.tile(eps, [mean.shape[0],1])
out = eps * tf.exp(logvar * .5) + mean
else:
out = tf.exp(logvar * .5) + mean
return out
def encode_conds(self, uv_template, uv_body_posed, uv_curv, training):
if self.only_curvatures:
z_cond = self.curv_enc(uv_curv, training=training)
else:
z_body = self.body_enc(uv_body_posed, training=training)
z_static = self.static_enc(uv_template, training=training)
if self.use_curvature:
z_curv = self.curv_enc(uv_curv, training=training)
z_cond = tf.concat([z_static, z_body, z_curv], axis=-1)
else:
z_cond = tf.concat([z_static, z_body], axis=-1)
return z_cond
def call(self, inputs, training):
uv_unposed, uv_template, uv_body_posed, uv_curv, uv_mask = inputs
if self.only_curvatures:
x = tf.concat([uv_unposed, uv_curv], axis=-1)
else:
x = tf.concat([uv_unposed, uv_template, uv_body_posed, uv_curv], axis=-1)
mean, logvar = self.encoder(x, training=training)
z = self.reparameterize(mean, logvar, training)
z = self.reshape(z)
z_cond = self.encode_conds(uv_template, uv_body_posed, uv_curv, training=training)
if self.add_noise and training == True:
z_cond = z_cond + (tf.random.uniform(z_cond.shape, maxval=self.noise_amount * 2) - self.noise_amount)
z = tf.concat([z, z_cond], axis=-1)
x = self.decoder(z, training=training)
if self.avg_garment is not None:
x = x + self.avg_garment[tf.newaxis,:,:,:]
if self.use_offsets_over_body:
x = x + uv_body_posed
x = x * uv_mask[:,:,:,tf.newaxis]
return x, mean, logvar
+166 -254
View File
@@ -1,21 +1,20 @@
import os import os
import sys import sys
from configs.config_abl_all_post import config sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/..')
import importlib
import argparse
parser = argparse.ArgumentParser(description='')
parser.add_argument('--name', type=str)
parser.add_argument('--config', type=str)
args = vars(parser.parse_args())
config_module = importlib.import_module(args['config'])
config = config_module.config
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = config['gpu'] os.environ["CUDA_VISIBLE_DEVICES"] = config['gpu']
import tensorflow as tf # os.environ["CUDA_LAUNCH_BLOCKING"] = '1'
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
try:
# Currently, memory growth needs to be the same across GPUs
for gpu in gpus:
tf.config.experimental.set_memory_growth(gpu, True)
logical_gpus = tf.config.experimental.list_logical_devices('GPU')
print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
except RuntimeError as e:
# Memory growth must be set before GPUs have been initialized
print(e)
import time import time
import argparse import argparse
@@ -26,256 +25,205 @@ import numpy as np
import plotly.graph_objects as go import plotly.graph_objects as go
import plotly.express as ex import plotly.express as ex
from PIL import Image from PIL import Image
from utils.postprocess import fix_collision
if config['is_skirt']: if config['is_skirt']:
from utils.losses import edge_loss_skirt as edge_loss from losses.losses import edge_loss_skirt as edge_loss
else: else:
from utils.losses import edge_loss from losses.losses import edge_loss
from utils.losses import collision_ratio, kl_loss, normal_loss, collision_loss import torch
from custom_utils.custom_utils import batch_to_dict, load_model, plot_3d, UV2Mesh, get_summary_html, maskFacesBatch, _maskFaces, plot_3d_collision import torch.nn as nn
from data.dataset import DatasetFactory, StaticDataset import torch.optim as optim
from custom_utils.smpl import SMPL from data.dataset import Dataset
from models.cvae_model import CVAENew as CVAE from models.cond_encoder import AutoEncoder
from utils.utils import Accumulator from utils.checkpoints import CheckpointSaver, load_model
from utils.utils import InfiniteDataLoader as DataLoader
from utils.postprocess import fix_collision
from losses.losses import collision_loss, normal_loss
from pytorch_msssim import MS_SSIM
from models.cvae_model import CVAE
from smpl.smpl_torch import SMPL
from custom_utils.custom_utils import UV2Mesh, Accumulator, plot_3d, get_summary_html, maskFacesBatch, get_summary_str, plot_3d_collision
def init_wandb(): def init_wandb():
run = wandb.init(project='ablation', name=config['name'], notes=config['notes'], dir='/wandb', tags=config['tags']) run = wandb.init(project='iccv', name=config['name'], notes=config['notes'], dir='/tmp', tags=config['tags'])
artifact = wandb.Artifact('code', type='code') artifact = wandb.Artifact('code', type='code')
config_file_names = [key for key in sys.modules.keys() if (key.startswith('configs.') or key.startswith('custom_utils.') or key.startswith('utils.') or key.startswith('data.') or key.startswith('models.') or key.startswith('scripts.'))] artifact.add_file('scripts/cvae.py')
artifact.add_file('losses/losses.py')
artifact.add_file('models/base.py')
artifact.add_file('models/cond_encoder.py')
artifact.add_file('models/convnext.py')
artifact.add_file('models/cvae_model.py')
artifact.add_file('data/dataset.py')
config_file_names = [key for key in sys.modules.keys() if key.startswith('configs.')]
for config_file_name in config_file_names: for config_file_name in config_file_names:
file_name = '/'.join(config_file_name.split('.')) + '.py' file_name = '/'.join(config_file_name.split('.')) + '.py'
if os.path.exists(file_name): if os.path.exists(file_name):
artifact.add_file(file_name) artifact.add_file(file_name)
run.log_artifact(artifact) run.log_artifact(artifact)
uv2mesh = UV2Mesh(res=256, is_skirt=config['is_skirt'], is_adaptive=False, is_smpl_orig=config['is_cape']) uv2mesh = UV2Mesh(res=256, is_skirt=config['is_skirt'], is_smpl_orig=config['is_cape'])
def kl_loss(mean, logvar): def kl_loss(mean: torch.Tensor, logvar: torch.Tensor) -> torch.Tensor:
kl_loss = 1 + logvar - tf.square(mean) - tf.exp(logvar) kl_loss = 1 + logvar - torch.square(mean) - torch.exp(logvar)
kl_loss = tf.reduce_mean(kl_loss) kl_loss = torch.mean(kl_loss)
kl_loss *= -0.5 kl_loss *= -0.5
return kl_loss return kl_loss
# 3d loss on posed !!!!!!!!!!!!
# @tf.function
def train_step(batch): def train_step(batch):
with tf.GradientTape() as tape:
if config['use_skinning']: if config['use_skinning']:
mesh_orig, uv_orig = batch['mesh_unposed'], batch['uv_unposed'] mesh_orig, uv_orig = batch['mesh_unposed'], batch['uv_unposed']
else: else:
mesh_orig, uv_orig = batch['mesh_posed'], batch['uv_posed'] mesh_orig, uv_orig = batch['mesh_posed'], batch['uv_posed']
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']], training=True) model.train()
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']])
mesh_pred = uv2mesh(pred) mesh_pred = uv2mesh(pred)
if config['use_skinning']: if config['use_skinning']:
shape = batch['pose_shape'][:, 72:] shape = batch['pose_shape'][:, 72:]
gender = tf.cast(tf.greater_equal(shape[:, 10] , 0.5), tf.int32) gender = torch.greater_equal(shape[:, 10] , 0.5).to(torch.long)
shape_offsets = tf.stack([tf.gather(tf.tensordot(shape[i,:10][tf.newaxis, :], smpl.shapedirs[gender[i]], axes=[[1], [2]])[0,:,:], smpl.to_keep, axis=0) for i in range(shape.shape[0])]) shape_offsets = torch.stack([torch.tensordot(shape[i,:10][None, :], smpl.shapedirs[gender[i]], dims=[[1], [2]])[0,smpl.to_keep,:] for i in range(shape.shape[0])])
# shape_offsets = torch.stack([torch.matmul(shape[i, :10], smpl.shapedirs[gender[i]][:, smpl.to_keep]) for i in range(shape.shape[0])])
mesh_pred_shaped = mesh_pred + shape_offsets * batch['mesh_mask'][:,:,tf.newaxis] mesh_pred_shaped = mesh_pred + shape_offsets * batch['mesh_mask'][:,:,None]
mesh_pred_posed = smpl.run_garment(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_pred_shaped) mesh_pred_posed = smpl(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_pred_shaped)[0]
else: else:
mesh_pred_posed = mesh_pred mesh_pred_posed = mesh_pred
# is the output already masked?
e_loss = tf.reduce_mean(edge_loss(mesh_pred, mesh_orig, batch['mesh_mask'])) e_loss = torch.mean(edge_loss(mesh_pred, mesh_orig, batch['mesh_mask']))
c_loss, c_ratio = collision_loss(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask']) c_loss, c_ratio = collision_loss(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask'])
c_loss, c_ratio = tf.reduce_mean(c_loss), tf.reduce_mean(c_ratio) c_loss, c_ratio = torch.mean(c_loss), torch.mean(c_ratio)
l2_loss = tf.reduce_mean(1e3 * tf.reduce_mean(tf.reduce_sum(((uv_orig - pred) ** 2), axis=-1), axis=[1, 2])) l2_loss = torch.mean(1e3 * torch.mean(torch.sum(((uv_orig - pred) ** 2), axis=-1), axis=[1, 2]))
l2_mesh_loss = tf.reduce_mean(1e3 * tf.reduce_mean(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (batch['mesh_posed'] - mesh_pred_posed) ** 2, axis=-1), axis=-1)) l2_mesh_loss = torch.mean(1e3 * torch.mean(torch.sum(batch['mesh_mask'][:,:,None] * (batch['mesh_posed'] - mesh_pred_posed) ** 2, axis=-1), axis=-1))
n_loss = normal_loss(mesh_pred_posed, batch['mesh_posed'], batch['mesh_mask'], is_skirt=config['is_skirt'], is_smpl_orig=config['is_cape']) n_loss = normal_loss(mesh_pred_posed, batch['mesh_posed'], batch['mesh_mask'], is_skirt=config['is_skirt'], is_smpl_orig=config['is_cape'])
# conc_loss = concept_loss(pred, batch['uv_unposed'])
kl = kl_loss(mean, logvar) kl = kl_loss(mean, logvar)
reconstr_loss = l2_loss reconstr_loss = l2_loss
if config['use_extra_losses']: if config['use_extra_losses']:
if 'block_collision_loss' in config.keys() and config['block_collision_loss'] == True: reconstr_loss = reconstr_loss + l2_mesh_loss + (e_loss + c_loss + n_loss) * config['reg_loss_coeff']
reconstr_loss = reconstr_loss + l2_mesh_loss + (e_loss) * config['reg_loss_coeff']
else:
reconstr_loss = reconstr_loss + l2_mesh_loss + (e_loss + c_loss) * config['reg_loss_coeff']
if config['use_norm_loss'] and step > 2000:
reconstr_loss = reconstr_loss + n_loss * config['reg_loss_coeff']
loss = reconstr_loss + g_balancing_coeff * kl loss = reconstr_loss + g_balancing_coeff * kl
mean = tf.reduce_mean(mean) optimizer.zero_grad()
logvar = tf.reduce_mean(logvar) loss.backward()
optimizer.step()
model.eval()
with torch.inference_mode():
mean = torch.mean(mean)
logvar = torch.mean(logvar)
ms_ssim = torch.mean(_MSSSIM(pred + 1, batch['uv_static'] + 1))
metric = torch.sum(torch.sqrt(torch.sum(batch['mesh_mask'][:,:,None] * (mesh_orig - mesh_pred.detach()) ** 2, axis=-1))) / torch.sum(batch['mesh_mask'])
# ms_ssim = tf.reduce_mean(tf.image.ssim_multiscale(pred + 5, batch['uv_unposed'] + 5, max_val=10, power_factors=(0.5, 0.2, 0.1, 0.1, 0.1)))
metric = tf.reduce_sum(tf.math.sqrt(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (mesh_orig - mesh_pred) ** 2, axis=-1))) / tf.reduce_sum(batch['mesh_mask'])
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
return { 'train/loss': loss, 'train/edge_loss': e_loss, 'train/collision_loss': c_loss, 'train/euclid_error': metric, return { 'train/loss': loss, 'train/edge_loss': e_loss, 'train/collision_loss': c_loss, 'train/euclid_error': metric,
'train/reconstr_loss': reconstr_loss, 'train/collision_ratio': c_ratio, 'train/reconstr_loss': reconstr_loss, 'train/collision_ratio': c_ratio,
'train/mean': mean, 'train/logvar': logvar, 'train/kl_loss': kl, 'train/var': np.e ** logvar, 'train/mean': mean, 'train/logvar': logvar, 'train/kl_loss': kl, 'train/var': np.e ** logvar,
'train/l2_loss': l2_loss, 'train/l2_mesh_loss': l2_mesh_loss, 'train/normal_loss': n_loss, 'train/l2_loss': l2_loss, 'train/l2_mesh_loss': l2_mesh_loss, 'train/normal_loss': n_loss,
# 'train/ms_ssim': ms_ssim, 'train/ms_ssim': ms_ssim,
} }
@tf.function
def valid_step(batch): def valid_step(batch):
if config['use_skinning']: if config['use_skinning']:
mesh_orig, uv_orig = batch['mesh_unposed'], batch['uv_unposed'] mesh_orig, uv_orig = batch['mesh_unposed'], batch['uv_unposed']
else: else:
mesh_orig, uv_orig = batch['mesh_posed'], batch['uv_posed'] mesh_orig, uv_orig = batch['mesh_posed'], batch['uv_posed']
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']], training=False) model.eval()
with torch.inference_mode():
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']])
mesh_pred = uv2mesh(pred) mesh_pred = uv2mesh(pred)
if config['use_skinning']: if config['use_skinning']:
shape = batch['pose_shape'][:, 72:] shape = batch['pose_shape'][:, 72:]
gender = tf.cast(tf.greater_equal(shape[:, 10] , 0.5), tf.int32) gender = torch.greater_equal(shape[:, 10] , 0.5).to(torch.long)
shape_offsets = tf.stack([tf.gather(tf.tensordot(shape[i,:10][tf.newaxis, :], smpl.shapedirs[gender[i]], axes=[[1], [2]])[0,:,:], smpl.to_keep, axis=0) for i in range(shape.shape[0])]) shape_offsets = torch.stack([torch.tensordot(shape[i,:10][None, :], smpl.shapedirs[gender[i]], dims=[[1], [2]])[0,smpl.to_keep,:] for i in range(shape.shape[0])])
# shape_offsets = torch.stack([torch.matmul(shape[i, :10], smpl.shapedirs[gender[i]][:, smpl.to_keep]) for i in range(shape.shape[0])])
mesh_pred_shaped = mesh_pred + shape_offsets * batch['mesh_mask'][:,:,tf.newaxis] mesh_pred_shaped = mesh_pred + shape_offsets * batch['mesh_mask'][:,:,None]
mesh_pred_posed = smpl.run_garment(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_pred_shaped) mesh_pred_posed = smpl(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_pred_shaped)[0]
else: else:
mesh_pred_posed = mesh_pred mesh_pred_posed = mesh_pred
# is the output already masked?
e_loss = tf.reduce_mean(edge_loss(mesh_pred, mesh_orig, batch['mesh_mask'])) e_loss = torch.mean(edge_loss(mesh_pred, mesh_orig, batch['mesh_mask']))
c_loss, c_ratio = collision_loss(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask']) c_loss, c_ratio = collision_loss(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask'])
c_loss, c_ratio = tf.reduce_mean(c_loss), tf.reduce_mean(c_ratio) c_loss, c_ratio = torch.mean(c_loss), torch.mean(c_ratio)
l2_loss = tf.reduce_mean(1e3 * tf.reduce_mean(tf.reduce_sum(((uv_orig - pred) ** 2), axis=-1), axis=[1, 2])) l2_loss = torch.mean(1e3 * torch.mean(torch.sum(((uv_orig - pred) ** 2), axis=-1), axis=[1, 2]))
l2_mesh_loss = tf.reduce_mean(1e3 * tf.reduce_mean(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (batch['mesh_posed'] - mesh_pred_posed) ** 2, axis=-1), axis=-1)) l2_mesh_loss = torch.mean(1e3 * torch.mean(torch.sum(batch['mesh_mask'][:,:,None] * (batch['mesh_posed'] - mesh_pred_posed) ** 2, axis=-1), axis=-1))
n_loss = normal_loss(mesh_pred_posed, batch['mesh_posed'], batch['mesh_mask'], is_skirt=config['is_skirt'], is_smpl_orig=config['is_cape']) n_loss = normal_loss(mesh_pred_posed, batch['mesh_posed'], batch['mesh_mask'], is_skirt=config['is_skirt'], is_smpl_orig=config['is_cape'])
# conc_loss = concept_loss(pred, batch['uv_unposed'])
kl = kl_loss(mean, logvar) kl = kl_loss(mean, logvar)
reconstr_loss = l2_loss reconstr_loss = l2_loss
if config['use_extra_losses']: if config['use_extra_losses']:
reconstr_loss = reconstr_loss + l2_mesh_loss + (e_loss + c_loss) * config['reg_loss_coeff'] reconstr_loss = reconstr_loss + l2_mesh_loss + (e_loss + c_loss + n_loss) * config['reg_loss_coeff']
if config['use_norm_loss']:
reconstr_loss = reconstr_loss + n_loss * config['reg_loss_coeff']
loss = reconstr_loss + g_balancing_coeff * kl loss = reconstr_loss + g_balancing_coeff * kl
mean = tf.reduce_mean(mean) mean = torch.mean(mean)
logvar = tf.reduce_mean(logvar) logvar = torch.mean(logvar)
ms_ssim = torch.mean(_MSSSIM(pred + 1, batch['uv_static'] + 1))
metric = torch.sum(torch.sqrt(torch.sum(batch['mesh_mask'][:,:,None] * (mesh_orig - mesh_pred.detach()) ** 2, axis=-1))) / torch.sum(batch['mesh_mask'])
# ms_ssim = tf.reduce_mean(tf.image.ssim_multiscale(pred + 5, batch['uv_unposed'] + 5, max_val=10, power_factors=(0.5, 0.2, 0.1, 0.1, 0.1)))
metric = tf.reduce_sum(tf.math.sqrt(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (mesh_orig - mesh_pred) ** 2, axis=-1))) / tf.reduce_sum(batch['mesh_mask'])
# c_ratio = tf.reduce_mean(collision_ratio(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask']))
return { 'valid/loss': loss, 'valid/edge_loss': e_loss, 'valid/collision_loss': c_loss, 'valid/euclid_error': metric, return { 'valid/loss': loss, 'valid/edge_loss': e_loss, 'valid/collision_loss': c_loss, 'valid/euclid_error': metric,
'valid/reconstr_loss': reconstr_loss, 'valid/l2_mesh_loss': l2_mesh_loss, 'valid/collision_ratio': c_ratio, 'valid/reconstr_loss': reconstr_loss, 'valid/l2_mesh_loss': l2_mesh_loss, 'valid/collision_ratio': c_ratio,
'valid/mean': mean, 'valid/logvar': logvar, 'valid/kl_loss': kl, 'valid/var': np.e ** logvar, 'valid/mean': mean, 'valid/logvar': logvar, 'valid/kl_loss': kl, 'valid/var': np.e ** logvar,
'valid/normal_loss': n_loss,# 'valid/ms_ssim': ms_ssim, 'valid/normal_loss': n_loss, 'valid/ms_ssim': ms_ssim,
} }
@tf.function
def test_step(batch):
if config['use_skinning']:
mesh_orig, uv_orig = batch['mesh_unposed'], batch['uv_unposed']
else:
mesh_orig, uv_orig = batch['mesh_posed'], batch['uv_posed']
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']], training=False)
mesh_pred = uv2mesh(pred)
if config['use_skinning']:
shape = batch['pose_shape'][:, 72:]
gender = tf.cast(tf.greater_equal(shape[:, 10] , 0.5), tf.int32)
shape_offsets = tf.stack([tf.gather(tf.tensordot(shape[i,:10][tf.newaxis, :], smpl.shapedirs[gender[i]], axes=[[1], [2]])[0,:,:], smpl.to_keep, axis=0) for i in range(shape.shape[0])])
mesh_pred_shaped = mesh_pred + shape_offsets * batch['mesh_mask'][:,:,tf.newaxis]
mesh_pred_posed = smpl.run_garment(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_pred_shaped)
mesh_orig_shaped = mesh_orig + shape_offsets * batch['mesh_mask'][:,:,tf.newaxis]
mesh_orig_posed = smpl.run_garment(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_orig_shaped)
else:
mesh_pred_posed = mesh_pred
mesh_orig_posed = mesh_orig
mesh_pred_posed_fixed = fix_collision(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask'])
# is the output already masked?
e_loss = tf.reduce_mean(edge_loss(mesh_pred, mesh_orig, batch['mesh_mask']))
c_loss, c_ratio = collision_loss(mesh_pred_posed, batch['mesh_body_posed'], batch['mesh_mask'])
c_loss, c_ratio = tf.reduce_mean(c_loss), tf.reduce_mean(c_ratio)
l2_loss = tf.reduce_mean(1e3 * tf.reduce_mean(tf.reduce_sum(((uv_orig - pred) ** 2), axis=-1), axis=[1, 2]))
l2_mesh_loss = tf.reduce_mean(1e3 * tf.reduce_mean(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (mesh_orig - mesh_pred) ** 2, axis=-1), axis=-1))
# conc_loss = concept_loss(pred, batch['uv_unposed'])
kl = kl_loss(mean, logvar)
reconstr_loss = l2_loss
if config['use_extra_losses']:
reconstr_loss = reconstr_loss + l2_mesh_loss + (e_loss + c_loss) * config['reg_loss_coeff']
loss = reconstr_loss + g_balancing_coeff * kl
mean = tf.reduce_mean(mean)
logvar = tf.reduce_mean(logvar)
ms_ssim = tf.reduce_mean(tf.image.ssim_multiscale(pred + 5, uv_orig + 5, max_val=10, power_factors=(0.5, 0.2, 0.1, 0.1, 0.1)))
metric = tf.reduce_sum(tf.math.sqrt(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (mesh_orig - mesh_pred) ** 2, axis=-1))) / tf.reduce_sum(batch['mesh_mask'])
euclid_posed = tf.reduce_sum(tf.math.sqrt(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (mesh_orig_posed - mesh_pred_posed) ** 2, axis=-1))) / tf.reduce_sum(batch['mesh_mask'])
euclid_posed_fixed = tf.reduce_sum(tf.math.sqrt(tf.reduce_sum(batch['mesh_mask'][:,:,tf.newaxis] * (mesh_orig_posed - mesh_pred_posed_fixed) ** 2, axis=-1))) / tf.reduce_sum(batch['mesh_mask'])
return { 'test/loss': loss, 'test/edge_loss': e_loss, 'test/collision_loss': c_loss, 'test/euclid_error': metric, 'test/ms_ssim': ms_ssim,
'test/reconstr_loss': reconstr_loss, 'test/collision_ratio': c_ratio, 'test/l2_mesh_loss': l2_mesh_loss,
'test/mean': mean, 'test/logvar': logvar, 'test/kl_loss': kl, 'test/var': np.e ** logvar,
'test/euclid_posed': euclid_posed, 'test/euclid_posed_fixed': euclid_posed_fixed }
def plot_everything(batch: dict, type: str): def plot_everything(batch: dict, type: str):
if config['use_skinning']: if config['use_skinning']:
mesh_orig, uv_orig = batch['mesh_unposed'], batch['uv_unposed'] mesh_orig, uv_orig = batch['mesh_unposed'].cpu().numpy(), batch['uv_unposed']
else: else:
mesh_orig, uv_orig = batch['mesh_posed'], batch['uv_posed'] mesh_orig, uv_orig = batch['mesh_posed'].cpu().numpy(), batch['uv_posed']
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']], training=False) model.eval()
with torch.inference_mode():
pred, mean, logvar = model([uv_orig, batch['uv_static'], batch['uv_body_posed'], batch['uv_curvature'], batch['uv_mask']])
mesh_pred = uv2mesh(pred).numpy() mesh_pred = uv2mesh(pred).detach().cpu().numpy()
orig_img = uv_orig orig_img = torch.permute(uv_orig, [0,2,3,1])
uv_static_img = batch['uv_static'] uv_static_img = torch.permute(batch['uv_static'], [0,2,3,1])
uv_body_posed_img = batch['uv_body_posed'] uv_body_posed_img = torch.permute(batch['uv_body_posed'], [0,2,3,1])
pred_img = pred pred_img = torch.permute(pred, [0,2,3,1])
img = tf.concat((uv_static_img, uv_body_posed_img, orig_img, pred_img), axis=2) img = torch.concat((uv_static_img, uv_body_posed_img, orig_img, pred_img), axis=2)
imgs = [wandb.Image((img[i] + 1) / 2.0) for i in range(4)] imgs = [wandb.Image((img[i].cpu().numpy() + 1) / 2.0) for i in range(4)]
F = np.load("custom_utils/smpl_faces" + skirt_modifier + ("_orig" if config['is_cape'] else "") + ".npy") F = np.load("smpl/smpl_faces" + skirt_modifier + ("_orig" if config['is_cape'] else "") + ".npy")
mask = batch['mesh_mask'].numpy().astype(bool) mask = batch['mesh_mask'].cpu().numpy().astype(bool)
if config['use_skinning']: if config['use_skinning']:
shape = batch['pose_shape'][:, 72:] shape = batch['pose_shape'][:, 72:]
gender = tf.cast(tf.greater_equal(shape[:, 10] , 0.5), tf.int32) gender = torch.greater_equal(shape[:, 10] , 0.5).to(torch.long)
shape_offsets = tf.stack([tf.gather(tf.tensordot(shape[i,:10][tf.newaxis, :], smpl.shapedirs[gender[i]], axes=[[1], [2]])[0,:,:], smpl.to_keep, axis=0) for i in range(shape.shape[0])]) shape_offsets = torch.stack([torch.tensordot(shape[i,:10][None, :], smpl.shapedirs[gender[i]], dims=[[1], [2]])[0,smpl.to_keep,:] for i in range(shape.shape[0])]).cpu().numpy()
garment_unposed, faces = maskFacesBatch(mesh_pred, F, mask) garment_unposed, faces = maskFacesBatch(mesh_pred, F, mask)
garment_unposed = [garment_unposed[i] + shape_offsets.numpy()[i, mask[i], :] for i in range(mask.shape[0])] garment_unposed = [garment_unposed[i] + shape_offsets[i, mask[i], :] for i in range(mask.shape[0])]
gt = [mesh_orig[i].numpy()[mask[i], :] + shape_offsets.numpy()[i, mask[i], :] for i in range(pred.shape[0])] gt = [mesh_orig[i][mask[i], :] + shape_offsets[i, mask[i], :] for i in range(pred.shape[0])]
for i in range(mesh_orig.shape[0]): for i in range(mesh_orig.shape[0]):
mesh_pred[i, mask[i], :] += shape_offsets.numpy()[i, mask[i], :] mesh_pred[i, mask[i], :] += shape_offsets[i, mask[i], :]
mesh_posed = smpl.run_garment(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], mesh_pred).numpy() mesh_posed = smpl(batch['pose_shape'][:, :72], batch['pose_shape'][:, 72:], torch.tensor(mesh_pred, device=config['device']))[0].cpu().numpy()
else: else:
mesh_posed = mesh_pred mesh_posed = mesh_pred.cpu().numpy()
gt = [mesh_orig[i].numpy()[mask[i], :] for i in range(pred.shape[0])] gt = [mesh_orig[i][mask[i], :] for i in range(pred.shape[0])]
garment_unposed, faces = maskFacesBatch(mesh_pred, F, mask) garment_unposed, faces = maskFacesBatch(mesh_pred.cpu().numpy(), F, mask)
gt_posed = [batch['mesh_posed'].numpy()[i, mask[i], :] for i in range(mask.shape[0])] gt_posed = [batch['mesh_posed'].cpu().numpy()[i, mask[i], :] for i in range(mask.shape[0])]
body = batch['mesh_body_posed'].numpy() mesh_posed_fixed = fix_collision(torch.tensor(mesh_posed, device=config['device']), batch['mesh_body_posed'], torch.tensor(mask, device=config['device'])).cpu().numpy()
mesh_posed_fixed = fix_collision(mesh_posed, body, mask).numpy() body = batch['mesh_body_posed'].cpu().numpy()
garment_posed = [mesh_posed[i, mask[i], :] for i in range(body.shape[0])] garment_posed = [mesh_posed[i, mask[i], :] for i in range(body.shape[0])]
collision = [np.concatenate((body[i], garment_posed[i]), axis=0) for i in range(len(garment_posed))] collision = [np.concatenate((body[i], garment_posed[i]), axis=0) for i in range(len(garment_posed))]
@@ -289,8 +237,8 @@ def plot_everything(batch: dict, type: str):
figs = { type + "/garment_" + str(i): plot_3d(gt[i], garment_unposed[i], faces[i], collision[i], collision_faces[i], collision_color[i], flip_xy=(not config['use_skinning'] and config['remove_global_orientation'])) for i in range(4) } figs = { type + "/garment_" + str(i): plot_3d(gt[i], garment_unposed[i], faces[i], collision[i], collision_faces[i], collision_color[i], flip_xy=(not config['use_skinning'] and config['remove_global_orientation'])) for i in range(4) }
figs_coll = { type + "/garment_" + str(i) + '_collision': plot_3d_collision(collision_gt[i], collision[i], collision_fixed[i], collision_faces[i], collision_color[i], flip_xy=(not config['use_skinning'] and config['remove_global_orientation'])) for i in range(4) } figs_coll = { type + "/garment_" + str(i) + '_collision': plot_3d_collision(collision_gt[i], collision[i], collision_fixed[i], collision_faces[i], collision_color[i], flip_xy=(not config['use_skinning'] and config['remove_global_orientation'])) for i in range(4) }
mean_hist = wandb.Histogram(sequence=mean.numpy().mean(axis=0)) mean_hist = wandb.Histogram(sequence=mean.cpu().numpy().mean(axis=0))
logvar_hist = wandb.Histogram(sequence=logvar.numpy().mean(axis=0)) logvar_hist = wandb.Histogram(sequence=logvar.cpu().numpy().mean(axis=0))
return { **figs, **figs_coll, **{ type + '/images': imgs, type + '/mean_hist': mean_hist, type + '/logvar_hist': logvar_hist } } return { **figs, **figs_coll, **{ type + '/images': imgs, type + '/mean_hist': mean_hist, type + '/logvar_hist': logvar_hist } }
@@ -414,6 +362,7 @@ def sample(folder):
print() print()
def test(): def test():
str_split = config['name'].split('_') str_split = config['name'].split('_')
test_idx = str_split.index('test') test_idx = str_split.index('test')
@@ -462,108 +411,71 @@ def test():
if __name__ == '__main__': def train():
# init_wandb() valid_iterator = iter(valid_dataloader)
parser = argparse.ArgumentParser(description='')
parser.add_argument('--name', type=str)
args = vars(parser.parse_args())
smpl = SMPL(orig_smpl=config['is_cape']) step = 0
for batch in train_dataloader:
train_loss = train_step(batch)
train_accumulator.update(train_loss)
if step % 25 == 0:
data_valid = next(valid_iterator)
valid_loss = valid_step(data_valid)
valid_accumulator.update(valid_loss)
if step % 1000 == 0 or (step < 1000 and step % 100 == 0):
to_log = dict({ **train_loss, **valid_loss, **{ 'current_step': step } })
if step % 5000 == 0 or (step < 5000 and step % 1000 == 0):
train_figs = plot_everything(batch, 'train')
valid_figs = plot_everything(data_valid, 'valid')
to_log = { **to_log, **train_figs, **valid_figs }
if step == 0:
to_log = { **to_log, **{ 'model/summary': wandb.Html(get_summary_html(model)) } }
wandb.log(to_log, step=step)
train_accumulator.reset()
valid_accumulator.reset()
print(step)
if train_loss['train/euclid_error'] < (config['balancing_activation_limit'] if 'balancing_activation_limit' in config.keys() else 0.017):
if balancing_activated_step == 0:
balancing_activated_step = step
g_balancing_coeff = g_balancing_coeff + 1 / 10000
if step % 10000 == 0:
ckpt_saver(model, step, optimizer)
step += 1
if __name__ == '__main__':
init_wandb()
# parser = argparse.ArgumentParser(description='')
# parser.add_argument('--name', type=str)
# parser.add_argument('--debug', action='store_true')
# args = vars(parser.parse_args())
smpl = SMPL(orig_smpl=config['is_cape'], path='smpl', device=config['device'])
skirt_modifier = '_skirt' if config['is_skirt'] else '' skirt_modifier = '_skirt' if config['is_skirt'] else ''
factory = DatasetFactory() dataset_train = Dataset(config['train_dir'], config['signature'], device=config['device'], frames_file=config['train_frames'], use_cache=config['dataset_in_memory'])
dataset_valid = Dataset(config['valid_dir'], config['signature'], device=config['device'], frames_file=config['valid_frames'], use_cache=config['dataset_in_memory'])
dataset_train = factory.get_dataset(folder=config['train_dir'], train_dataloader = DataLoader(dataset_train, batch_size=4, drop_last=True, shuffle=True, )
batch_size=config['batch_size'], valid_dataloader = DataLoader(dataset_valid, batch_size=4, drop_last=True, shuffle=True)
signature=config['signature_dynamic'],
resolutions=[config['resolution']],
in_memory=config['dataset_in_memory'],
use_one_hot=config['use_one_hot'],
use_no_orient=config['remove_global_orientation'],
frames_file=config['frames_file'] if 'frames_file' in config.keys() else None,
is_cape=config['is_cape'])
static_dataset_train = StaticDataset(folder=config['train_dir'],
signature=config['signature_static'],
resolutions=[config['resolution']],
in_memory=True,
is_cape=config['is_cape'])
dataset_valid = factory.get_dataset(folder=config['valid_dir'],
batch_size=config['batch_size'],
signature=config['signature_dynamic'],
resolutions=[config['resolution']],
in_memory=config['dataset_in_memory'],
use_one_hot=config['use_one_hot'],
use_no_orient=config['remove_global_orientation'],
frames_file=config['frames_file_valid'] if 'frames_file_valid' in config.keys() else None,
is_cape=config['is_cape'])
static_dataset_valid = StaticDataset(folder=config['valid_dir'],
signature=config['signature_static'],
resolutions=[config['resolution']],
in_memory=True,
is_cape=config['is_cape'])
train_accumulator = Accumulator() train_accumulator = Accumulator()
valid_accumulator = Accumulator() valid_accumulator = Accumulator()
model = CVAE(config) model = CVAE(config)
optimizer = tf.optimizers.Adam(learning_rate=config['lr']) optimizer = optim.Adam(model.parameters(), lr=config['lr'])
_MSSSIM = MS_SSIM(data_range=2, size_average=True, channel=3)
balancing_activated_step = 0 balancing_activated_step = 0
g_balancing_coeff = config['starting_balancing_coeff'] g_balancing_coeff = config['starting_balancing_coeff']
# sample('checkpoints/cvae_13') ckpt_saver = CheckpointSaver('checkpoints/iccv/' + config['name'], top_n=5)
# test()
ckpt = tf.train.Checkpoint(model=model)
manager = tf.train.CheckpointManager(ckpt, 'checkpoints/post/' + config['name'], max_to_keep=20)
if config['pretrained_model'] != '': if config['pretrained_model'] != '':
load_model(model, config['pretrained_model']) load_model(model, config['pretrained_model'])
# load_model(model, 'checkpoints/' + config['name']) train()
valid_iterator = iter(dataset_valid)
step = 0
for batch in dataset_train:
batch = static_dataset_train.expand_batch(batch_to_dict(batch, config['signature_dynamic']))
train_loss = train_step(batch)
train_accumulator.update(train_loss)
data_valid = valid_iterator.get_next()
data_valid = static_dataset_valid.expand_batch(batch_to_dict(data_valid, config['signature_dynamic']))
valid_loss = valid_step(data_valid)
valid_accumulator.update(valid_loss)
if train_accumulator.get()['train/euclid_error'] < (config['balancing_activation_limit'] if 'balancing_activation_limit' in config.keys() else 0.017):
if balancing_activated_step == 0:
balancing_activated_step = step
g_balancing_coeff = g_balancing_coeff + 1 / 10000
if step % 1000 == 0 or (step < 1000 and step % 100 == 0):
to_log = { **train_accumulator.get(), **valid_accumulator.get(), **{ 'current_step': step} }
if step % 5000 == 0 or (step < 5000 and step % 1000 == 0):
train_figs = plot_everything(batch, 'train')
valid_figs = plot_everything(data_valid, 'valid')
to_log = { **to_log, **train_figs, **valid_figs }
if step == 0:
to_log = { **to_log, **{ 'model/summary': wandb.Html(get_summary_html(model)) } }
to_log = { **to_log, **{ 'train/balancing_coeff': g_balancing_coeff } }
wandb.log(to_log, step = step)
train_accumulator.reset()
valid_accumulator.reset()
if step % 5000 == 0:
manager.save(checkpoint_number=step)
print(step)
step += 1
+1 -1
View File
@@ -218,7 +218,7 @@ class SMPL(Module):
result = result - root_joint result = result - root_joint
joints = joints - root_joint joints = joints - root_joint
if remove: if remove and verts is None:
result = result[:,self.to_keep,:] result = result[:,self.to_keep,:]
return result, joints return result, joints
+16 -26
View File
@@ -1,55 +1,45 @@
import os import os
# os.environ["CUDA_VISIBLE_DEVICES"] = "3"
import tensorflow as tf
import sys import sys
import shutil import shutil
import tqdm import tqdm
import random import random
import math import math
import torch
import numpy as np import numpy as np
import scipy as sp import scipy as sp
import scipy.io as sio import scipy.io as sio
import plotly.graph_objects as go import plotly.graph_objects as go
import plotly.express as ex import plotly.express as ex
from pytorch3d.structures import Meshes
from custom_utils.custom_utils import _maskFaces from custom_utils.custom_utils import _maskFaces
from losses.losses import _nearest_neighbour
from scipy.spatial import cKDTree
from tensorflow_graphics.geometry.representation.mesh.normals import vertex_normals
# from hbertiche/PBNS # from hbertiche/PBNS
""" COLLISION FUNCTIONS START """ """ COLLISION FUNCTIONS START """
F = np.load("custom_utils/smpl_faces.npy") # F = np.load("smpl/smpl_faces.npy")
to_keep = sio.loadmat('smpl/keep_remove.mat')['keep'].reshape(-1)
F_fix_coll = torch.tensor(np.tile(np.load("smpl/smpl_faces.npy")[to_keep,:][None,:,:], [4,1,1]), device='cuda')
def _nearest_neighbour(V, B):
tree = cKDTree(B.numpy())
return tree.query(V.numpy(), workers=-1)[1]
@tf.function
def _tf_nn_parallel(V, B):
return tf.map_fn(fn=lambda elem: tf.py_function(func=_nearest_neighbour, inp=elem, Tout=tf.int32), elems=(V,B), fn_output_signature=tf.int32, parallel_iterations=V.shape[0])
def fix_collision(V, B, mask, thr=.004): def fix_collision(V, B, mask, thr=.004):
B_vn = vertex_normals(B, tf.tile(F[None], [B.shape[0], 1, 1])) mesh = Meshes(B, F_fix_coll)
B_vn = torch.stack(mesh.verts_normals_list())
# compute correspondences # compute correspondences
idx = _tf_nn_parallel(V, B) idx = _nearest_neighbour(V, B)
N, M = V.shape[:2]
# _idx = tf.tile(np.array(list(range(N)), np.int32)[:,None], [1, M])
_idx = tf.ones((N, M), dtype=tf.int32) * np.array(list(range(N)), np.int32)[:,None]
idx = tf.stack((_idx, idx), axis=-1)
# compute loss # compute loss
D = V - tf.gather_nd(B, idx) B_idx = torch.stack([B[i,idx[i],:] for i in range(V.shape[0])])
b_vn = tf.gather_nd(B_vn, idx) D = V - B_idx
dot = tf.einsum('abc,abc->ab', D, b_vn) b_vn = torch.stack([B_vn[i,idx[i],:] for i in range(V.shape[0])])
loss = tf.minimum(dot - thr, 0) * mask dot = torch.einsum('abc,abc->ab', D, b_vn)
B_offset_closest = tf.gather_nd(B, idx) + (b_vn / 2e2) loss = torch.minimum(dot - thr, torch.zeros_like(dot)) * mask
output = tf.where(loss[:,:,tf.newaxis] < 0, B_offset_closest, V) B_offset_closest = B_idx + (b_vn / 2e2)
output = torch.where(loss[:,:,None] < 0, B_offset_closest, V)
return output return output