Spaces:
Sleeping
Sleeping
File size: 10,024 Bytes
c8f05dd | 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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | # ============================================================
# 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,
}
|