# 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])