| import torch |
| import torch.nn as nn |
|
|
| class DualPoolMiniConv8x8(nn.Module): |
| def __init__(self): |
| super(DualPoolMiniConv8x8, self).__init__() |
| |
| 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 = self.conv(x) |
| |
| 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() |
| |
| |
| self.geo_net = nn.Sequential( |
| nn.Linear(956, 256), |
| nn.LayerNorm(256), |
| nn.ReLU(inplace=True), |
| nn.Linear(256, 256), |
| nn.ReLU(inplace=True) |
| ) |
| |
| |
| 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): |
| |
| |
| batch_size = patches.shape[0] |
| |
| |
| 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) |
|
|