multi-camera-bev / models /bev_encoder.py
Dheepak Karan
BEV perception demo: PyTorch SimpleBEV on ZeroGPU
d78fec2
Raw History Blame Contribute Delete
4.65 kB
# ============================================================
# bev_encoder.py
# ============================================================
#
# ETHUKU CREATE PANNINOM?
# ━━━━━━━━━━━━━━━━━━━━━━
# LSS kuduthathu "raw" BEV - just camera features-ai kotti vachathu.
# Antha map-la neighbour cells-ku thodarbu (context) illa.
# Ithu antha map-ai innum smart-a maathum: "intha 4m neelamana blob =
# oru car", "intha vari = road".
#
# MUNADI FILE ODA CONNECTION:
# ━━━━━━━━━━━━━━━━━━━━━━━━━━
# Input : view_transformer.py output [B,64,200,200]
# Output: center_head.py ku [B,128,200,200]
#
# INNER OPERATIONS:
# ━━━━━━━━━━━━━━━━
# ResNet-style downsample (200->100->50) -> aprom upsample thirumba 200.
# Yaen keezha-mela? Downsample panna oru pixel periya area paakkum
# (receptive field). Athu illaina 12m lorry-va oru pixel-la puriya mudiyathu.
# FPN-style: chinna map-ai periya map-oda koottrom (details + context rendum).
#
# INPUT / OUTPUT:
# ━━━━━━━━━━━━━━
# Input : [B, 64, 200, 200]
# Output: [B, 128, 200, 200]
#
# EPADI USE AAGUM:
# ━━━━━━━━━━━━━━━
# Detection head-ku rich features kudukka.
#
# ============================================================
import torch
import torch.nn as nn
import torch.nn.functional as F
from data.scripts.constants import BACKBONE_OUT_CHANNELS, BEV_OUT_CHANNELS
class BasicBlock(nn.Module):
"""ResNet basic block: conv-bn-relu x2 + skip connection."""
def __init__(self, in_ch: int, out_ch: int, stride: int = 1):
super().__init__()
self.conv1 = nn.Conv2d(in_ch, out_ch, 3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(out_ch)
self.conv2 = nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(out_ch)
# Skip connection: input-ai output-oda neradiya koottrom.
# Yaen? Deep network-la gradient vanish aagum. Skip = "highway"
# gradient-ku, so training stable.
# Shape match aagalaina 1x1 conv poattu adjust pannurom.
self.shortcut = nn.Identity()
if stride != 1 or in_ch != out_ch:
self.shortcut = nn.Sequential(
nn.Conv2d(in_ch, out_ch, 1, stride=stride, bias=False),
nn.BatchNorm2d(out_ch),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
out = F.relu(self.bn1(self.conv1(x)), inplace=True)
out = self.bn2(self.conv2(out))
return F.relu(out + self.shortcut(x), inplace=True)
class BEVEncoder(nn.Module):
"""ResNet-18 style encoder + FPN-style decoder on the BEV map."""
def __init__(self, in_channels: int = BACKBONE_OUT_CHANNELS,
out_channels: int = BEV_OUT_CHANNELS):
super().__init__()
# --- Encoder (keezha poguthu) ---
self.layer1 = BasicBlock(in_channels, 64, stride=1) # [B,64,200,200]
self.layer2 = BasicBlock(64, 128, stride=2) # [B,128,100,100]
self.layer3 = BasicBlock(128, 256, stride=2) # [B,256,50,50]
# --- Decoder (mela varuthu) ---
# lateral = 1x1 conv, channel count-ai samam pannurathu
self.lat3 = nn.Conv2d(256, out_channels, 1)
self.lat2 = nn.Conv2d(128, out_channels, 1)
self.lat1 = nn.Conv2d(64, out_channels, 1)
# smooth = upsample panna vandha "blocky" artifacts-ai sari pannum
self.smooth = nn.Sequential(
nn.Conv2d(out_channels, out_channels, 3, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.ReLU(inplace=True),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
Args:
x: [B, 64, 200, 200] raw BEV from LSS
Returns:
[B, 128, 200, 200] encoded BEV features
"""
c1 = self.layer1(x) # [B, 64, 200, 200] fine details
c2 = self.layer2(c1) # [B,128, 100, 100] medium context
c3 = self.layer3(c2) # [B,256, 50, 50] large context
# Top-down: periya context-ai chinna details-oda koottrom
p3 = self.lat3(c3) # 50x50
p2 = self.lat2(c2) + F.interpolate(p3, scale_factor=2, mode="bilinear",
align_corners=False) # 100x100
p1 = self.lat1(c1) + F.interpolate(p2, scale_factor=2, mode="bilinear",
align_corners=False) # 200x200
return self.smooth(p1) # [B, 128, 200, 200]