Spaces:
Running on Zero
Running on Zero
Download viz/classes.py from akashch1512/SingleViewHeigthEstimation: direct link, hf CLI and curl.
- Browser
- Download file 2.48 kB
-
https://huggingface.co/spaces/akashch1512/SingleViewHeigthEstimation/resolve/main/viz/classes.py
- Command line
-
hf download hf://spaces/akashch1512/SingleViewHeigthEstimation/viz/classes.py
-
curl -L -o classes.py https://huggingface.co/spaces/akashch1512/SingleViewHeigthEstimation/resolve/main/viz/classes.py
2.48 kB
| """Class-map and object previews: the two PNGs a person reads to trust `objects.json`. | |
| * `class_preview` — the seg head's class map blended over the optical image. | |
| * `objects_overlay` — detected tree crowns, building footprints and water | |
| outlines drawn on the optical image. This is the check for the object step: | |
| a crown circle that does not sit on a tree is visible at a glance. | |
| Pure numpy + Pillow, like `figures.colorize` / `figures.hillshade`. The palette | |
| matches the frontend's "Object classes" layer so the two read the same. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| from PIL import Image, ImageDraw | |
| CLASS_COLORS: dict[str, tuple[int, int, int]] = { | |
| "other": (154, 160, 166), | |
| "ground": (200, 181, 138), | |
| "low_veg": (156, 204, 101), | |
| "building": (224, 90, 71), | |
| "water": (58, 143, 217), | |
| "road": (95, 99, 104), | |
| "tree": (46, 125, 50), | |
| } | |
| _UNKNOWN = (154, 160, 166) | |
| def colorize_classes(seg: np.ndarray, names: tuple[str, ...]) -> np.ndarray: | |
| """(H, W) class ids -> (H, W, 3) uint8, by class *name* so ids can move.""" | |
| lut = np.array([CLASS_COLORS.get(n, _UNKNOWN) for n in names] + [_UNKNOWN] * 256, | |
| np.uint8)[:256] | |
| return lut[np.clip(seg, 0, 255).astype(np.uint8)] | |
| def class_preview(rgb: np.ndarray, seg: np.ndarray, names: tuple[str, ...], | |
| alpha: float = 0.6) -> Image.Image: | |
| color = colorize_classes(seg, names).astype(np.float32) | |
| out = alpha * color + (1.0 - alpha) * rgb.astype(np.float32) | |
| return Image.fromarray(np.clip(out, 0, 255).astype(np.uint8)) | |
| def objects_overlay(rgb: np.ndarray, objects: dict) -> Image.Image: | |
| """Crowns as circles of their radius, footprints and water as outlines.""" | |
| gsd = float(objects["grid"]["gsd_m"]) | |
| base = Image.fromarray((rgb.astype(np.float32) * 0.7).astype(np.uint8)) | |
| draw = ImageDraw.Draw(base) | |
| line = max(1, round(max(rgb.shape[:2]) / 800)) | |
| for w in objects["water"]: | |
| draw.polygon([tuple(pt) for pt in w["poly"]], outline=CLASS_COLORS["water"], width=line) | |
| for b in objects["buildings"]: | |
| draw.polygon([tuple(pt) for pt in b["poly"]], outline=(255, 120, 90), width=line) | |
| for t in objects["trees"]: | |
| r = t["r"] / gsd | |
| x, y = t["x"], t["y"] | |
| draw.ellipse((x - r, y - r, x + r, y + r), outline=(120, 230, 120), width=line) | |
| d = max(1.0, line) | |
| draw.ellipse((x - d, y - d, x + d, y + d), fill=(255, 255, 255)) | |
| return base | |