yolozyk's picture
update generate tools
c3ec853
Raw
History Blame Contribute Delete
9.34 kB
import torch
import torch.nn as nn
import torch.nn.functional as F
def get_crop_config(crop):
"""
Get configuration parameters based on crop size.
Args:
crop: List of [height, width] or None
Returns:
dict: Configuration including alpha, h, w, diffx, diffy
"""
if crop is None:
# Default values for TIP dataset (56x40)
return {
'alpha': 6,
'h': 3,
'w': 2,
'diffx': [1, 0, 0, 0],
'diffy': [1, 0, 0, 0]
}
elif crop == [64, 27]: # PressurePose dataset
return {
'alpha': 4,
'h': 4,
'w': 1,
'diffx': [1, 0, 1, 1],
'diffy': [0, 0, 0, 0]
}
elif crop == [110, 37]: # MOYO dataset
return {
'alpha': 12,
'h': 6,
'w': 2,
'diffx': [0, 1, 0, 1],
'diffy': [1, 1, 1, 0]
}
else: # Default for other sizes
return {
'alpha': 6,
'h': 3,
'w': 2,
'diffx': [1, 0, 0, 0],
'diffy': [1, 0, 0, 0]
}
class DoubleConv(nn.Module):
"""Double Convolutional Block with BN and ReLU"""
def __init__(self, in_channels, out_channels, mid_channels=None):
super().__init__()
if not mid_channels:
mid_channels = out_channels
self.double_conv = nn.Sequential(
nn.Conv2d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),
nn.BatchNorm2d(mid_channels),
nn.ReLU(inplace=True),
nn.Conv2d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True)
)
def forward(self, x):
return self.double_conv(x)
class Down(nn.Module):
"""Downscaling with maxpool then double conv"""
def __init__(self, in_channels, out_channels):
super().__init__()
self.maxpool_conv = nn.Sequential(
nn.MaxPool2d(2),
DoubleConv(in_channels, out_channels)
)
def forward(self, x):
return self.maxpool_conv(x)
class Up(nn.Module):
"""Upscaling then double conv"""
def __init__(self, in_channels, out_channels, bilinear=True):
super().__init__()
if bilinear:
self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
else:
self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
self.conv = DoubleConv(in_channels, out_channels)
def forward(self, x1, x2):
x1 = self.up(x1)
diffY = x2.size()[2] - x1.size()[2]
diffX = x2.size()[3] - x1.size()[3]
x1 = F.pad(x1, [diffX // 2, diffX - diffX // 2, diffY // 2, diffY - diffY // 2])
x = torch.cat([x2, x1], dim=1)
return self.conv(x)
class OutConv(nn.Module):
def __init__(self, in_channels, out_channels):
super(OutConv, self).__init__()
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=1)
def forward(self, x):
return self.conv(x)
class ConditionalBatchNorm2d(nn.Module):
"""Conditional BatchNorm2d to modulate output with condition vector"""
def __init__(self, num_features, cond_dim):
super(ConditionalBatchNorm2d, self).__init__()
self.num_features = num_features
self.bn = nn.BatchNorm2d(num_features, affine=False)
self.gamma = nn.Linear(cond_dim, num_features)
self.beta = nn.Linear(cond_dim, num_features)
def forward(self, x, cond):
gamma = self.gamma(cond).view(-1, self.num_features, 1, 1)
beta = self.beta(cond).view(-1, self.num_features, 1, 1)
return self.bn(x) * gamma + beta
class UpWithCondition(nn.Module):
"""Upscaling then double conv with condition modulation"""
def __init__(self, in_channels, out_channels, cond_dim, diffX, diffY, bilinear=True):
super(UpWithCondition, self).__init__()
self.diffX = diffX
self.diffY = diffY
if bilinear:
self.up = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
self.conv = DoubleConv(in_channels, out_channels, in_channels // 2)
else:
self.up = nn.ConvTranspose2d(in_channels, in_channels // 2, kernel_size=2, stride=2)
self.conv = DoubleConv(in_channels // 2, out_channels)
# Conditional batch norm for output modulation
self.cond_bn = ConditionalBatchNorm2d(out_channels, cond_dim)
def forward(self, x, cond):
x = self.up(x)
x = F.pad(x, [self.diffX // 2, self.diffX - self.diffX // 2, self.diffY // 2, self.diffY - self.diffY // 2])
x = self.conv(x)
x = self.cond_bn(x, cond) # Modulate with condition
return x
class UNetEncoder(nn.Module):
def __init__(self, cond_dim=256, embed_dim=256, dp_rate=0.0, bilinear=False, crop=None):
super(UNetEncoder, self).__init__()
self.cond_dim = cond_dim
self.crop = crop
# Get crop-specific configuration
crop_config = get_crop_config(crop)
alpha = crop_config['alpha']
# Encoder
self.inc = DoubleConv(1, 64)
self.down1 = Down(64, 128)
self.down2 = Down(128, 256)
self.down3 = Down(256, 512)
factor = 2 if bilinear else 1
self.down4 = Down(512, 1024 // factor)
self.dropout = nn.Dropout(dp_rate)
# VAE latent space parameters
self.fc_mu = nn.Linear((1024 // factor) * alpha + cond_dim, embed_dim)
self.fc_log_var = nn.Linear((1024 // factor) * alpha + cond_dim, embed_dim)
def forward(self, x, cond):
# Encoder path
x1 = self.inc(x)
x2 = self.down1(x1)
x3 = self.down2(x2)
x4 = self.down3(x3)
x5 = self.down4(x4) # B x (1024 // factor) x H x W
# Flatten and concatenate with condition vector
x5_flat = x5.view(x5.size(0), -1)
x5_cond = torch.cat([x5_flat, cond], dim=1)
# Compute mu and log_var for latent space
mu = self.fc_mu(x5_cond)
log_var = self.fc_log_var(x5_cond)
return mu, log_var
class UNetDecoder(nn.Module):
def __init__(self, cond_dim=256, embed_dim=256, dp_rate=0.0, bilinear=False, crop=None):
super(UNetDecoder, self).__init__()
self.bilinear = bilinear
self.cond_dim = cond_dim
self.crop = crop
# Get crop-specific configuration
crop_config = get_crop_config(crop)
alpha = crop_config['alpha']
self.h = crop_config['h']
self.w = crop_config['w']
diffx = crop_config['diffx']
diffy = crop_config['diffy']
factor = 2 if bilinear else 1
# Map latent vector and condition back to decoder size
self.fc_z = nn.Linear(embed_dim + cond_dim, (1024 // factor) * alpha)
self.dropout = nn.Dropout(dp_rate)
# Decoder with conditional batch norm
self.up1 = UpWithCondition(1024, 512 // factor, cond_dim, diffX=diffx[0], diffY=diffy[0], bilinear=bilinear)
self.up2 = UpWithCondition(512, 256 // factor, cond_dim, diffX=diffx[1], diffY=diffy[1], bilinear=bilinear)
self.up3 = UpWithCondition(256, 128 // factor, cond_dim, diffX=diffx[2], diffY=diffy[2], bilinear=bilinear)
self.up4 = UpWithCondition(128, 64, cond_dim, diffX=diffx[3], diffY=diffy[3], bilinear=bilinear)
self.outc = OutConv(64, 1)
def forward(self, z, cond):
b, _ = z.shape
factor = 2 if self.bilinear else 1
# Decode latent vector concatenated with condition
z_cond = torch.cat([z, cond], dim=1)
z_decoded = self.fc_z(z_cond).view(b, 1024 // factor, self.h, self.w)
z_decoded = self.dropout(z_decoded)
# Decoder path with conditional modulation
x = self.up1(z_decoded, cond)
x = self.up2(x, cond)
x = self.up3(x, cond)
x = self.up4(x, cond)
x = self.outc(x)
return x.view(b, self.crop[0], self.crop[1])
if __name__ == "__main__":
def reparameterize(mu, logvar):
"""VAE reparameterization trick"""
std = torch.exp(0.5 * logvar)
eps = torch.randn_like(std)
return mu + eps * std
# Test data
data = torch.randn(4, 1, 56, 40)
condition = torch.randn(4, 85)
# Initialize encoder and decoder
encoder = UNetEncoder(cond_dim=85, embed_dim=256, bilinear=False, crop=[56, 40])
decoder = UNetDecoder(cond_dim=85, embed_dim=256, bilinear=False, crop=[56, 40])
# Forward pass through encoder
mu, logvar = encoder(data, condition)
print(f"Mu shape: {mu.shape}, Logvar shape: {logvar.shape}")
# Reparameterization trick
z = reparameterize(mu, logvar)
print(f"Latent z shape: {z.shape}")
# Forward pass through decoder
reconstructed = decoder(z, condition)
print(f"Reconstructed shape: {reconstructed.shape}")
print(f"Expected shape: [4, 56, 40]")
assert reconstructed.shape == (4, 56, 40), f"Shape mismatch: {reconstructed.shape} vs (4, 56, 40)"
print("Test passed!")