File size: 5,894 Bytes
4c75661 | 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 | """๋ฎ ๋งต ์ฌ๊ตฌ์ฑ ์งํ๋ ๊ทธ๋ฆผ โ ์ง์ญ 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)
|