File size: 4,652 Bytes
d78fec2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# ============================================================
# 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]