Spaces:
Running
Running
| # Copyright (c) Meta Platforms, Inc. and affiliates. | |
| from typing import Dict, List | |
| import cv2 | |
| import numpy as np | |
| import torch | |
| from ..utils.geo import BoundaryBox | |
| from .data import MapArea, MapLine, MapNode | |
| from .parser import Groups | |
| class Canvas: | |
| def __init__(self, bbox: BoundaryBox, ppm: float): | |
| self.bbox = bbox | |
| self.ppm = ppm | |
| self.scaling = bbox.size * ppm | |
| self.w, self.h = np.ceil(self.scaling).astype(int) | |
| self.clear() | |
| def clear(self): | |
| self.raster = np.zeros((self.h, self.w), np.uint8) | |
| def to_uv(self, xy: np.ndarray): | |
| xy = self.bbox.normalize(xy) | |
| xy[..., 1] = 1 - xy[..., 1] | |
| s = self.scaling | |
| if isinstance(xy, torch.Tensor): | |
| s = torch.from_numpy(s).to(xy) | |
| return xy * s - 0.5 | |
| def to_xy(self, uv: np.ndarray): | |
| s = self.scaling | |
| if isinstance(uv, torch.Tensor): | |
| s = torch.from_numpy(s).to(uv) | |
| xy = (uv + 0.5) / s | |
| xy[..., 1] = 1 - xy[..., 1] | |
| return self.bbox.unnormalize(xy) | |
| def draw_polygon(self, xy: np.ndarray): | |
| uv = self.to_uv(xy) | |
| cv2.fillPoly(self.raster, uv[None].astype(np.int32), 255) | |
| def draw_multipolygon(self, xys: List[np.ndarray]): | |
| uvs = [self.to_uv(xy).round().astype(np.int32) for xy in xys] | |
| cv2.fillPoly(self.raster, uvs, 255) | |
| def draw_line(self, xy: np.ndarray, width: float = 1): | |
| uv = self.to_uv(xy) | |
| cv2.polylines( | |
| self.raster, uv[None].round().astype(np.int32), False, 255, thickness=width | |
| ) | |
| def draw_cell(self, xy: np.ndarray): | |
| if not self.bbox.contains(xy): | |
| return | |
| uv = self.to_uv(xy) | |
| self.raster[tuple(uv.round().astype(int).T[::-1])] = 255 | |
| def render_raster_masks( | |
| nodes: List[MapNode], | |
| lines: List[MapLine], | |
| areas: List[MapArea], | |
| canvas: Canvas, | |
| ) -> Dict[str, np.ndarray]: | |
| all_groups = Groups.areas + Groups.ways + Groups.nodes | |
| masks = {k: np.zeros((canvas.h, canvas.w), np.uint8) for k in all_groups} | |
| for area in areas: | |
| canvas.raster = masks[area.group] | |
| outlines = area.outers + area.inners | |
| canvas.draw_multipolygon(outlines) | |
| if area.group == "building": | |
| canvas.raster = masks["building_outline"] | |
| for line in outlines: | |
| canvas.draw_line(line) | |
| for line in lines: | |
| canvas.raster = masks[line.group] | |
| canvas.draw_line(line.xy) | |
| for node in nodes: | |
| canvas.raster = masks[node.group] | |
| canvas.draw_cell(node.xy) | |
| return masks | |
| def mask_to_idx(group2mask: Dict[str, np.ndarray], groups: List[str]) -> np.ndarray: | |
| masks = np.stack([group2mask[k] for k in groups]) > 0 | |
| void = ~np.any(masks, 0) | |
| idx = np.argmax(masks, 0) | |
| idx = np.where(void, np.zeros_like(idx), idx + 1) # add background | |
| return idx | |
| def render_raster_map(masks: Dict[str, np.ndarray]) -> np.ndarray: | |
| areas = mask_to_idx(masks, Groups.areas) | |
| ways = mask_to_idx(masks, Groups.ways) | |
| nodes = mask_to_idx(masks, Groups.nodes) | |
| return np.stack([areas, ways, nodes]) | |