multi-camera-bev / data /scripts /dataset.py
Dheepak Karan
Align the demo with what actually ships, and pull model code from the main repo
c8f05dd
Raw History Blame Contribute Delete
10 kB
# ============================================================
# dataset.py
# ============================================================
#
# ETHUKU CREATE PANNINOM?
# ━━━━━━━━━━━━━━━━━━━━━━
# PyTorch DataLoader-ku puriyara madhiri oru Dataset class venum.
# Rendu velai pannuthu:
# 1. Input taiyaar: 6 camera images + K + E (sample_loader use)
# 2. TARGET taiyaar: ground-truth boxes -> BEV map format
# (heatmap, offset, size, rot...) -> loss compare panna
#
# MUNADI FILE ODA CONNECTION:
# ━━━━━━━━━━━━━━━━━━━━━━━━━━
# sample_loader.load_sample() -> input side
# constants.py -> grid size, classes
# training/train.py ithai DataLoader-la wrap pannum.
#
# INNER OPERATIONS:
# ━━━━━━━━━━━━━━━━
# nuScenes boxes (LIDAR frame) -> ego frame -> BEV grid cell (200x200)
# -> antha cell-la gaussian blob varaiyurom (heatmap)
# -> regression values (offset, z, size, yaw, velocity) store pannurom.
#
# INPUT / OUTPUT:
# ━━━━━━━━━━━━━━
# __getitem__(i) -> dict with images/intrinsics/extrinsics + targets
#
# EPADI USE AAGUM:
# ━━━━━━━━━━━━━━━
# Model "intha cell-la car iruku" nu predict pannum.
# Namma target "aama/illa" nu solli, difference = loss.
#
# ============================================================
import numpy as np
import torch
from torch.utils.data import Dataset
from .constants import (
BEV_H, BEV_W, BEV_RESOLUTION, X_RANGE, Y_RANGE,
N_CLASSES, NUSCENES_NAME_MAP, CLASS_TO_IDX,
VAL_SCENES, DATA_ROOT, VERSION,
)
from .sample_loader import load_sample
def draw_gaussian(heatmap: np.ndarray, cx: int, cy: int, radius: int) -> None:
"""
Heatmap-la oru cell suthi "mellisaana veliccham" (gaussian) varaiyurathu.
Yaen point mattum illa, blob?
Object center exactly oru cell-la nikkathu - konjam adjacent cell-um
"kittathatta correct" thaan. So neighbours-ku konjam score kudukirom.
Ithu illaina training romba kastam (200x200 = 40000 cell-la 1 mattum
correct-nu solli model-ai therikka mudiyathu).
Args:
heatmap: [H, W] array, in-place modify aagum
cx, cy : center cell (column, row)
radius : blob size in cells
"""
diameter = 2 * radius + 1
sigma = diameter / 6.0 # standard CenterNet choice
# Chinna gaussian patch create pannurom
y, x = np.ogrid[-radius:radius + 1, -radius:radius + 1]
g = np.exp(-(x * x + y * y) / (2 * sigma * sigma)) # center=1.0, edge~0
g[g < np.finfo(g.dtype).eps * g.max()] = 0
H, W = heatmap.shape
# Grid edge-la object irundha patch veliya poidum -> clip pannurom
left, right = min(cx, radius), min(W - cx, radius + 1)
top, bottom = min(cy, radius), min(H - cy, radius + 1)
if right <= 0 or bottom <= 0 or left < 0 or top < 0:
return
masked_hm = heatmap[cy - top:cy + bottom, cx - left:cx + right]
masked_g = g[radius - top:radius + bottom, radius - left:radius + right]
# maximum: rendu object overlap aana, periya value-ai vachikirom
np.maximum(masked_hm, masked_g, out=masked_hm)
class NuScenesBEVDataset(Dataset):
"""
nuScenes mini -> BEV detection dataset.
Args:
data_root: "data/nuscenes-mini"
split: "train" or "val"
nusc: already-loaded NuScenes object (optional, reuse panna)
"""
def __init__(self, data_root: str = DATA_ROOT, split: str = "train", nusc=None):
from nuscenes.nuscenes import NuScenes
self.data_root = data_root
self.split = split
# NuScenes DB load pannurathu ~10 sec edukkum, so oru thadava mattum
self.nusc = nusc if nusc is not None else NuScenes(
version=VERSION, dataroot=data_root, verbose=False
)
# Scene level-la split pannurom (sample level illa).
# Yaen? Ore scene-la adjacent frames kittathatta same photo.
# Train-la oru frame, val-la adjacent frame irundha = cheating.
self.sample_tokens = []
for scene in self.nusc.scene:
is_val = scene["name"] in VAL_SCENES
if (split == "val") != is_val:
continue
token = scene["first_sample_token"]
while token:
self.sample_tokens.append(token)
token = self.nusc.get("sample", token)["next"]
def __len__(self) -> int:
return len(self.sample_tokens)
def _boxes_in_ego(self, sample_token: str) -> list:
"""
Antha sample-oda ellaa annotation box-aiyum EGO CAR frame-la thara.
Yaen ego frame? Namma camera extrinsics-um camera->ego thaan.
Rendum ore frame-la irundha thaan match aagum.
Returns:
list of dict: {cls, x, y, z, w, l, h, yaw, vx, vy}
x = pinnadi(-)/munnadi(+) metres, y = valathu(-)/idathu(+) metres
"""
from pyquaternion import Quaternion
sample = self.nusc.get("sample", sample_token)
# LIDAR_TOP sample_data -> ego pose reference-ku use pannurom
lidar_token = sample["data"]["LIDAR_TOP"]
sd = self.nusc.get("sample_data", lidar_token)
ego_pose = self.nusc.get("ego_pose", sd["ego_pose_token"])
ego_t = np.array(ego_pose["translation"])
ego_R_inv = Quaternion(ego_pose["rotation"]).inverse
boxes = []
for ann_token in sample["anns"]:
ann = self.nusc.get("sample_annotation", ann_token)
name = NUSCENES_NAME_MAP.get(ann["category_name"])
if name is None: # namma 10 class-la illa -> skip
continue
# Global (world) coords -> ego coords
center = np.array(ann["translation"]) - ego_t
center = ego_R_inv.rotate(center)
rot = ego_R_inv * Quaternion(ann["rotation"])
yaw = rot.yaw_pitch_roll[0] # top-down la yaw mattum thevai
w, l, h = ann["size"] # nuScenes order: width,length,height
vel = self.nusc.box_velocity(ann_token) # global m/s, nan varalam
if np.any(np.isnan(vel)):
vx, vy = 0.0, 0.0
else:
v_ego = ego_R_inv.rotate(vel)
vx, vy = float(v_ego[0]), float(v_ego[1])
boxes.append({
"cls": CLASS_TO_IDX[name],
"x": float(center[0]), "y": float(center[1]), "z": float(center[2]),
"w": float(w), "l": float(l), "h": float(h),
"yaw": float(yaw), "vx": vx, "vy": vy,
})
return boxes
def _build_targets(self, boxes: list) -> dict:
"""
Box list -> BEV target maps (model output-oda same shape).
Returns dict of tensors:
heatmap [10,200,200] 0..1, center-la 1.0
mask [1,200,200] 1 = inga object center iruku
offset [2,200,200] cell-uku ulla exact position (0..1)
height [1,200,200] z metres
size [3,200,200] log(w), log(l), log(h)
rot [2,200,200] sin(yaw), cos(yaw)
vel [2,200,200] vx, vy
"""
heatmap = np.zeros((N_CLASSES, BEV_H, BEV_W), dtype=np.float32)
mask = np.zeros((1, BEV_H, BEV_W), dtype=np.float32)
offset = np.zeros((2, BEV_H, BEV_W), dtype=np.float32)
height = np.zeros((1, BEV_H, BEV_W), dtype=np.float32)
size = np.zeros((3, BEV_H, BEV_W), dtype=np.float32)
rot = np.zeros((2, BEV_H, BEV_W), dtype=np.float32)
vel = np.zeros((2, BEV_H, BEV_W), dtype=np.float32)
for b in boxes:
# metres -> grid cell (float)
# Example: x = 0m -> (0 - (-50))/0.5 = 100 = grid center
# x = 10m (10m munnadi) -> (10+50)/0.5 = 120
fx = (b["x"] - X_RANGE[0]) / BEV_RESOLUTION
fy = (b["y"] - Y_RANGE[0]) / BEV_RESOLUTION
cx, cy = int(fx), int(fy)
if not (0 <= cx < BEV_W and 0 <= cy < BEV_H):
continue # 100x100m veliya -> skip
# Object evlo periyathu-nu paathu blob size decide
radius = max(2, int(min(b["w"], b["l"]) / BEV_RESOLUTION / 2))
draw_gaussian(heatmap[b["cls"]], cx, cy, radius)
mask[0, cy, cx] = 1.0
# int-la potta appuram missing aana decimal part.
# Ithu illaina 0.5m varai error (cell size).
offset[0, cy, cx] = fx - cx
offset[1, cy, cx] = fy - cy
height[0, cy, cx] = b["z"]
# log yaen? size 0.5m to 20m varai varum. log potta range
# chinnathaagum -> network kathukka easy.
size[0, cy, cx] = np.log(max(b["w"], 0.1))
size[1, cy, cx] = np.log(max(b["l"], 0.1))
size[2, cy, cx] = np.log(max(b["h"], 0.1))
# yaw-ai neradiya predict panna problem: 0 degree = 360 degree
# aana number-la romba different. sin/cos-la athu solve aagum.
rot[0, cy, cx] = np.sin(b["yaw"])
rot[1, cy, cx] = np.cos(b["yaw"])
vel[0, cy, cx] = b["vx"]
vel[1, cy, cx] = b["vy"]
return {
"heatmap": torch.from_numpy(heatmap),
"mask": torch.from_numpy(mask),
"offset": torch.from_numpy(offset),
"height": torch.from_numpy(height),
"size": torch.from_numpy(size),
"rot": torch.from_numpy(rot),
"vel": torch.from_numpy(vel),
}
def __getitem__(self, idx: int) -> dict:
token = self.sample_tokens[idx]
sample = load_sample(self.nusc, token, self.data_root) # inputs
targets = self._build_targets(self._boxes_in_ego(token)) # labels
return {
"images": sample["images"], # [6,3,224,400]
"intrinsics": sample["intrinsics"], # [6,3,3]
"extrinsics": sample["extrinsics"], # [6,4,4]
"targets": targets,
}