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