Spaces:
Sleeping
Sleeping
File size: 8,103 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 | # ============================================================
# make_demo.py
# ============================================================
#
# ETHUKU CREATE PANNINOM?
# ββββββββββββββββββββββ
# README-ku / LinkedIn-ku oru portfolio demo venum. Recruiter 5 second
# thaan paapaanga - antha 5 second-la "6 camera -> BEV detections" +
# "TensorRT 3.9x speedup" rendum theriyanum.
#
# bev_renderer.py oru frame varaiyum. Ithu:
# - thodarchiyaana frames (scene-la oru vari, so motion theriyum)
# - real T4 benchmark numbers-ai padathula-ye potrom
# - GIF + thani PNG rendum
#
# MUNADI FILE ODA CONNECTION:
# ββββββββββββββββββββββββββ
# visualization/bev_renderer.py -> draw_bev, denormalize reuse
# training/evaluate.py -> decode_predictions
#
# INPUT / OUTPUT:
# ββββββββββββββ
# Output: runs/demo/demo.gif + frame_*.png
#
# EPADI USE AAGUM:
# βββββββββββββββ
# python -m visualization.make_demo --frames 20
#
# ============================================================
import argparse
import os
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from data.scripts.constants import CAMERAS, CLASSES
from visualization.bev_renderer import denormalize, draw_bev, CLASS_COLORS
# Kaggle T4-la nijamaa alandha numbers. Hardcode pannurom yaen?
# Ithu Mac-la odum, aana kaatta vendiyathu T4 result. Mac-la
# TensorRT-e illa, so anga alandhadhai inga kondu varom.
BENCHMARK = [
("PyTorch FP32", 32.66, 30.6, "1.00x"),
("ONNX Runtime", 30.20, 33.1, "1.08x"),
("TensorRT FP32", 24.34, 41.1, "1.34x"), # <- ithu thaan ship aachu
("TensorRT FP16", 61.56, 16.2, "0.53x"),
("TensorRT INT8", 8.52, 117.3, "3.83x"),
]
SHIPPED = "TensorRT FP32"
def draw_stats_panel(ax) -> None:
"""
Benchmark table-ai padathula text-a varaiyurathu.
Yaen padathula? README-la thani table irundhaalum, GIF-ai
LinkedIn-la neradiya podumbodhu numbers kooda pogum.
"""
ax.axis("off")
ax.set_facecolor("#0a0a14")
y = 0.97
ax.text(0.5, y, "Tesla T4 | 6 cameras -> BEV", ha="center",
color="#e0e0e0", fontsize=11, weight="bold",
transform=ax.transAxes)
y -= 0.09
ax.text(0.02, y, f"{'Backend':<15}{'ms':>7}{'FPS':>7}{'vs base':>9}",
color="#8a8a9a", fontsize=8.5, family="monospace",
transform=ax.transAxes)
y -= 0.035
ax.plot([0.02, 0.98], [y, y], color="#2a2a3a", lw=0.8,
transform=ax.transAxes, clip_on=False)
for name, ms, fps, speed in BENCHMARK:
y -= 0.055
# Vegam jaasti INT8-ku thaan. Aana ship aanadhu FP32 -
# highlight pannurathu ship aanadhai, vegathai illa.
best = name == SHIPPED
color = "#00d4ff" if best else "#b0b0c0"
ax.text(0.02, y, f"{name:<15}{ms:>7.1f}{fps:>7.1f}{speed:>9}",
color=color, fontsize=8.5, family="monospace",
weight="bold" if best else "normal",
transform=ax.transAxes)
y -= 0.10
for line, col in [
("Shipped: TensorRT FP32", "#00d4ff"),
("1.34x faster, nothing given up", "#8a8a9a"),
("", "#6a6a7a"),
("INT8 was 3.83x and 16x smaller,", "#ff8c00"),
("but kept only 34% of the original", "#8a8a9a"),
("detections. Fast and wrong is worse", "#8a8a9a"),
("than slower and right.", "#8a8a9a"),
]:
ax.text(0.02, y, line, color=col, fontsize=8,
transform=ax.transAxes)
y -= 0.045
y -= 0.03
ax.plot([0.02, 0.98], [y, y], color="#2a2a3a", lw=0.8,
transform=ax.transAxes, clip_on=False)
y -= 0.055
for line in ["SimpleBEV 5.5M params",
"EfficientNet-B0 + Lift-Splat-Shoot",
"ResNet-18 FPN + CenterPoint head",
"nuScenes mini | no LiDAR"]:
ax.text(0.02, y, line, color="#6a6a7a", fontsize=7.5,
transform=ax.transAxes)
y -= 0.04
def render(images, gt_boxes, pred_boxes, out_path, frame_i, n_frames,
score_thresh=0.2):
"""Oru demo frame: cameras | BEV | stats."""
fig = plt.figure(figsize=(19, 7.5), facecolor="#0a0a14")
gs = fig.add_gridspec(3, 5, width_ratios=[0.78, 0.78, 0.1, 1.7, 0.95],
hspace=0.1, wspace=0.05)
layout = [("CAM_FRONT_LEFT", 0, 0), ("CAM_FRONT_RIGHT", 0, 1),
("CAM_FRONT", 1, 0), ("CAM_BACK", 1, 1),
("CAM_BACK_LEFT", 2, 0), ("CAM_BACK_RIGHT", 2, 1)]
for cam, r, c in layout:
ax = fig.add_subplot(gs[r, c])
ax.imshow(denormalize(images[CAMERAS.index(cam)]))
ax.set_title(cam.replace("CAM_", ""), color="#8a8a9a", fontsize=6.5, pad=1)
ax.axis("off")
ax_bev = fig.add_subplot(gs[:, 3])
draw_bev(ax_bev, gt_boxes, pred_boxes, score_thresh)
n = sum(1 for b in pred_boxes if b["score"] >= score_thresh)
ax_bev.set_title(f"Bird's Eye View | frame {frame_i+1}/{n_frames} | "
f"{n} detections", color="#e0e0e0", fontsize=10, pad=8)
present = sorted({b["cls"] for b in pred_boxes if b["score"] >= score_thresh})
if present:
handles = [plt.Line2D([0], [0], marker="s", linestyle="", markersize=6,
color=CLASS_COLORS[c], label=CLASSES[c])
for c in present]
ax_bev.legend(handles=handles, loc="upper left", fontsize=7,
facecolor="#14141e", edgecolor="#2a2a3a",
labelcolor="#c0c0d0")
draw_stats_panel(fig.add_subplot(gs[:, 4]))
fig.text(0.42, 0.02, "white dashed = ground truth | filled = prediction",
ha="center", color="#6a6a7a", fontsize=8)
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
fig.savefig(out_path, dpi=110, bbox_inches="tight", facecolor="#0a0a14")
plt.close(fig)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--ckpt", default="runs/simplebev/best.pth")
ap.add_argument("--data-root", default="data/nuscenes-mini")
ap.add_argument("--out-dir", default="runs/demo")
ap.add_argument("--frames", type=int, default=20)
ap.add_argument("--start", type=int, default=0)
ap.add_argument("--score-thresh", type=float, default=0.2)
ap.add_argument("--ms-per-frame", type=int, default=450)
args = ap.parse_args()
import torch
from data.scripts.dataset import NuScenesBEVDataset
from models.simplebev import SimpleBEV
from training.evaluate import decode_predictions
device = torch.device("mps" if torch.backends.mps.is_available() else "cpu")
model = SimpleBEV(pretrained=False).to(device).eval()
model.load_state_dict(torch.load(args.ckpt, map_location=device)["model"])
ds = NuScenesBEVDataset(args.data_root, split="val")
n = min(args.frames, len(ds) - args.start)
paths = []
for k in range(n):
i = args.start + k
s = ds[i]
with torch.no_grad():
preds = model(s["images"].unsqueeze(0).to(device),
s["intrinsics"].unsqueeze(0).to(device),
s["extrinsics"].unsqueeze(0).to(device))
boxes = decode_predictions({k2: v.cpu() for k2, v in preds.items()})[0]
gt = ds._boxes_in_ego(ds.sample_tokens[i])
p = os.path.join(args.out_dir, f"frame_{k:03d}.png")
render(s["images"].numpy(), gt, boxes, p, k, n, args.score_thresh)
paths.append(p)
print(f" frame {k+1}/{n}")
from PIL import Image
frames = [Image.open(p).convert("P", palette=Image.ADAPTIVE, colors=128)
for p in paths]
gif = os.path.join(args.out_dir, "demo.gif")
frames[0].save(gif, save_all=True, append_images=frames[1:],
duration=args.ms_per_frame, loop=0, optimize=True)
print(f"\nsaved {gif} ({os.path.getsize(gif)/1e6:.1f} MB, {n} frames)")
print(f"saved {n} PNGs in {args.out_dir}/")
if __name__ == "__main__":
main()
|