Gaze-LIPE / src /models /ablation_8x8.py
thanhhuyvan's picture
Initial release of LIPE V2 GOLD
a10ba7f
Raw
History Blame Contribute Delete
2.31 kB
import torch
import torch.nn as nn
class DualPoolMiniConv8x8(nn.Module):
def __init__(self):
super(DualPoolMiniConv8x8, self).__init__()
# Using padding=1 for 8x8 to preserve spatial information at the boundaries
self.conv = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=3, padding=1), nn.ReLU(inplace=True),
nn.BatchNorm2d(16),
nn.Conv2d(16, 32, kernel_size=3, padding=1), nn.ReLU(inplace=True),
nn.BatchNorm2d(32),
nn.Conv2d(32, 64, kernel_size=3, padding=1), nn.ReLU(inplace=True),
nn.BatchNorm2d(64)
)
self.avg_pool = nn.AdaptiveAvgPool2d(1)
self.max_pool = nn.AdaptiveMaxPool2d(1)
def forward(self, x):
# x: (Batch, 1, 8, 8)
x = self.conv(x)
# Output: (Batch, 128)
return torch.cat([self.avg_pool(x), self.max_pool(x)], dim=1).flatten(1)
class LIPEV2Ablation8x8(nn.Module):
def __init__(self):
super(LIPEV2Ablation8x8, self).__init__()
self.app_net = DualPoolMiniConv8x8()
# Geometry network (same as 16x16)
self.geo_net = nn.Sequential(
nn.Linear(956, 256),
nn.LayerNorm(256),
nn.ReLU(inplace=True),
nn.Linear(256, 256),
nn.ReLU(inplace=True)
)
# 64*2*4 (appearance) + 256 (geometry) = 512 + 256 = 768
self.post_concat_bn = nn.BatchNorm1d(768)
self.fusion = nn.Sequential(
nn.Linear(768, 256),
nn.ReLU(inplace=True),
nn.Dropout(0.1),
nn.Linear(256, 128),
nn.ReLU(inplace=True)
)
self.pitch_head = nn.Linear(128, 90)
self.yaw_head = nn.Linear(128, 90)
def forward(self, patches, landmarks):
# patches: (Batch, 4, 8, 8)
# landmarks: (Batch, 956)
batch_size = patches.shape[0]
# Process 4 patches per sample
app_feat = self.app_net(patches.view(-1, 1, 8, 8)).view(batch_size, -1)
geo_feat = self.geo_net(landmarks)
combined = torch.cat([app_feat, geo_feat], dim=1)
combined = self.post_concat_bn(combined)
fused = self.fusion(combined)
return self.pitch_head(fused), self.yaw_head(fused)