vkgs-sim / scripts /map_coverage.py
kimjunhan's picture
Add files using upload-large-folder tool
4c75661 verified
Raw History Blame Contribute Delete
5.89 kB
"""๋‚ฎ ๋งต ์žฌ๊ตฌ์„ฑ ์ง„ํ–‰๋„ ๊ทธ๋ฆผ โ€” ์ง€์—ญ 3๊ณณ์˜ ์ฃผํ–‰๊ฐ€๋Šฅ์˜์—ญ ์œ„์— ํ•™์Šต๋œ ๊ตฌ๊ฐ„(์ •์  PSNR ์ƒ‰)๊ณผ ์•„์ง ์•ˆ ๋œ ๋‚ฎ ์”ฌ(ํšŒ์ƒ‰) ๊ถค์ .
๋„๋กœ ์ปค๋ฒ„๋ฆฌ์ง€๋Š” recon.batch.coverage_order ์™€ ๊ฐ™์€ 10 m ์นธ ๊ธฐ์ค€ (๋‚ฎ ์”ฌ ์ž์ฐจ ๊ถค์ ์ด ์ง€๋‚˜๊ฐ„ ์นธ ์ค‘ ํ•™์Šต๋œ ๊ตฌ๊ฐ„์ด ๋งก๋Š” ์นธ).
GPU ๋ฅผ ์“ฐ์ง€ ์•Š๋Š”๋‹ค โ€” ํ•™์Šต์ด ๋„๋Š” ์ค‘์—๋„ ๋Œ๋ ค๋„ ๋œ๋‹ค.
์‹คํ–‰: source scripts/local_env.sh && python scripts/map_coverage.py [์ถœ๋ ฅ.png] (๊ธฐ๋ณธ outputs/coverage/map_coverage_<๋‚ ์งœ>.png)
"""
import json
import sys
import time
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import font_manager
from matplotlib.collections import LineCollection
from matplotlib.patches import Polygon
from nusim.data.index import load_scene
from nusim.maps.expansion import MapExpansion
from nusim.paths import output_dir
from nusim.recon.batch import COVER_CELL_M, day_scenes, segment_dirs
ROOT = output_dir() / "gs_day"
OUT = Path(sys.argv[1]) if len(sys.argv) > 1 else output_dir() / "coverage" / f"map_coverage_{time.strftime('%Y%m%d')}.png"
LOCS = [("singapore-onenorth", "One-North"), ("singapore-queenstown", "Queenstown"),
("singapore-hollandvillage", "Holland Village")]
FONT = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc"
if Path(FONT).exists():
font_manager.fontManager.addfont(FONT)
plt.rcParams["font.family"] = font_manager.FontProperties(fname=FONT).get_name()
PSNR_LO, PSNR_HI = 20.0, 28.0
def cells(xy: np.ndarray) -> set:
return set(map(tuple, np.floor(xy / COVER_CELL_M).astype(int)))
def scene_track(name: str) -> tuple[np.ndarray, np.ndarray]:
"""์ •๋ฉด ์นด๋ฉ”๋ผ ์ž์ฐจ ์œ„์น˜ (n,2) ์™€ ์”ฌ ์‹œ์ž‘๋ถ€ํ„ฐ์˜ ์ดˆ."""
fr = load_scene(name).frames["CAM_FRONT"]
xy = np.array([f.ego_translation[:2] for f in fr])
t = (np.array([f.timestamp for f in fr]) - fr[0].timestamp) / 1e6
return xy, t
def seg_psnr(d: Path) -> float | None:
p = d / "eval.json"
return json.loads(p.read_text()).get("psnr_static") if p.exists() else None
fig, axes = plt.subplots(1, 3, figsize=(24, 9.2), gridspec_kw={"width_ratios": [1.25, 1, 1]})
cmap = plt.get_cmap("viridis")
tot = {"scenes": 0, "done": 0, "segs": 0, "cells": 0, "cells_done": 0}
for ax, (loc, title) in zip(axes, LOCS):
names = day_scenes(loc)
all_xy, done_lines, done_c, todo_lines, done_cells, all_cells = [], [], [], [], set(), set()
n_done, n_segs = 0, 0
for s in names:
xy, t = scene_track(s)
all_xy.append(xy)
all_cells |= cells(xy)
segs = segment_dirs(ROOT, s)
if not segs:
todo_lines.append(xy)
continue
n_done += 1
owned = np.zeros(len(t), bool)
for info, d in segs:
n_segs += 1
o0 = info.get("own0") if info.get("own0") is not None else -1
o1 = info.get("own1") if info.get("own1") is not None else 1e9
m = (t >= o0) & (t <= o1)
owned |= m
if m.sum() >= 2:
done_lines.append(xy[m])
v = seg_psnr(d)
done_c.append(cmap((np.clip(v, PSNR_LO, PSNR_HI) - PSNR_LO) / (PSNR_HI - PSNR_LO)) if v else (0.5, 0.5, 0.5, 1))
done_cells |= cells(xy[m])
if (~owned).sum() >= 2: # ํ•™์Šต์ด ๋œ ๋๋‚œ ์”ฌ์˜ ๋‚˜๋จธ์ง€ ๊ตฌ๊ฐ„
todo_lines.append(xy[~owned])
pts = np.concatenate(all_xy)
lo, hi = pts.min(0) - 80, pts.max(0) + 80
m = MapExpansion(loc)
for rec in m._d["drivable_area"]:
for tok in rec["polygon_tokens"]:
rings = m.polygon_rings(tok)
if not rings:
continue
r0 = np.asarray(rings[0])
if r0[:, 0].max() < lo[0] or r0[:, 0].min() > hi[0] or r0[:, 1].max() < lo[1] or r0[:, 1].min() > hi[1]:
continue
ax.add_patch(Polygon(r0, closed=True, fc="#E9ECF1", ec="none", zorder=0))
for h in rings[1:]:
ax.add_patch(Polygon(np.asarray(h), closed=True, fc="white", ec="none", zorder=0))
ax.add_collection(LineCollection(todo_lines, colors="#B9BEC7", linewidths=1.2, zorder=1))
ax.add_collection(LineCollection(done_lines, colors=done_c, linewidths=3.2, zorder=2, capstyle="round"))
ax.set_xlim(lo[0], hi[0]); ax.set_ylim(lo[1], hi[1]); ax.set_aspect("equal")
ax.set_xticks([]); ax.set_yticks([])
cov = len(done_cells & all_cells) / max(len(all_cells), 1)
ax.set_title(f"{title}\n์”ฌ {n_done}/{len(names)} ยท ๊ตฌ๊ฐ„ {n_segs}๊ฐœ ยท ๋„๋กœ ์ปค๋ฒ„๋ฆฌ์ง€ {cov:.0%}", fontsize=17)
sb = 200.0
ax.plot([lo[0] + 30, lo[0] + 30 + sb], [lo[1] + 30] * 2, color="k", lw=3)
ax.text(lo[0] + 30 + sb / 2, lo[1] + 45, "200 m", ha="center", fontsize=12)
for k, v in (("scenes", len(names)), ("done", n_done), ("segs", n_segs), ("cells", len(all_cells)),
("cells_done", len(done_cells & all_cells))):
tot[k] += v
print(f"{loc}: ์”ฌ {n_done}/{len(names)}, ๊ตฌ๊ฐ„ {n_segs}, ๋„๋กœ ์นธ {len(done_cells & all_cells)}/{len(all_cells)} ({cov:.1%})")
sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(PSNR_LO, PSNR_HI))
cb = fig.colorbar(sm, ax=axes, orientation="horizontal", fraction=0.035, pad=0.03, aspect=60)
cb.set_label("ํ•™์Šต๋œ ๊ตฌ๊ฐ„์˜ ์ •์  PSNR (dB) โ€” ํšŒ์ƒ‰ ์„ : ์•„์ง ํ•™์Šต ์•ˆ ๋œ ๋‚ฎ ์”ฌ ๊ถค์  ยท ๋ฐ”ํƒ•: ์ฃผํ–‰๊ฐ€๋Šฅ์˜์—ญ", fontsize=13)
fig.suptitle(f"์‹ฑ๊ฐ€ํฌ๋ฅด ๋‚ฎ ๋งต ์žฌ๊ตฌ์„ฑ ์ง„ํ–‰ โ€” {time.strftime('%Y-%m-%d %H:%M')} ยท ์”ฌ {tot['done']}/{tot['scenes']} ยท "
f"๊ตฌ๊ฐ„ {tot['segs']}๊ฐœ ยท ๋„๋กœ ์ปค๋ฒ„๋ฆฌ์ง€ {tot['cells_done'] / tot['cells']:.0%} (10 m ์นธ {tot['cells_done']}/{tot['cells']})",
fontsize=20, fontweight="bold")
OUT.parent.mkdir(parents=True, exist_ok=True)
fig.savefig(OUT, dpi=110, bbox_inches="tight")
print("saved", OUT)