refactor(sync): improve topdown rendering and make justfile adapt to windows

This commit is contained in:
2026-04-06 19:54:23 +08:00
parent 2c78f4b662
commit eb016385f8
3 changed files with 65 additions and 44 deletions

View File

@@ -1,17 +1,20 @@
remote_root := "ial-gpu-workstation-1:/home/ial-pangyg/docker-workspace/projects/mini-nav"
rsync_flags := "-avLh --progress --stats --itemize-changes"
upload_excludes := "--exclude=.jj/ --exclude=.git/ --exclude=.devenv/ --exclude=.direnv/ --exclude=deps/ --exclude=outputs/ --exclude=data/versioned_data/ --exclude=datasets/"
upload: upload:
rsync -avLh --progress --stats --itemize-changes \ if command -v cygpath >/dev/null 2>&1 || test -n "${MSYSTEM:-}" || test -n "${CYGWIN:-}"; then \
--exclude='.jj/' \ env MSYS2_ARG_CONV_EXCL='*' rsync {{ rsync_flags }} {{ upload_excludes }} . {{ remote_root }}/; \
--exclude='.git/' \ else \
--exclude='.devenv/' \ rsync {{ rsync_flags }} {{ upload_excludes }} . {{ remote_root }}/; \
--exclude='.direnv/' \ fi
--exclude='deps/' \
--exclude='outputs/' \
--exclude='data/versioned_data/' \
. ial-gpu-workstation-1:/home/ial-pangyg/docker-workspace/projects/mini-nav/
download: download:
rsync -avLh --progress --stats --itemize-changes \ if command -v cygpath >/dev/null 2>&1 || test -n "${MSYSTEM:-}" || test -n "${CYGWIN:-}"; then \
ial-gpu-workstation-1:/home/ial-pangyg/docker-workspace/projects/mini-nav/outputs . env MSYS2_ARG_CONV_EXCL='*' rsync {{ rsync_flags }} {{ remote_root }}/outputs .; \
else \
rsync {{ rsync_flags }} {{ remote_root }}/outputs .; \
fi
sync-pkgs: sync-pkgs:
# micromamba env create -f ./environment.yml # micromamba env create -f ./environment.yml

View File

@@ -1,9 +1,13 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from importlib import import_module
from typing import Any, Sequence from typing import Any, Sequence
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg
from matplotlib.figure import Figure
from PIL import Image
@dataclass(frozen=True) @dataclass(frozen=True)
class TopDownSceneElements: class TopDownSceneElements:
@@ -28,10 +32,26 @@ def render_topdown_scene_map(
elements: TopDownSceneElements, elements: TopDownSceneElements,
meters_per_pixel: float, meters_per_pixel: float,
style: TopDownRenderStyle | None = None, style: TopDownRenderStyle | None = None,
maps_module: Any | None = None, ) -> Image.Image:
plt_module: Any | None = None, """Render a top-down scene map as a PIL Image.
use_matplotlib: bool = True,
) -> Any: Uses matplotlib Agg backend (non-interactive) to draw room markers and
labels on the habitat top-down occupancy map, then returns the result
as a PIL Image.
Args:
pathfinder: Habitat pathfinder instance.
elements: Scene elements containing room nodes to annotate.
meters_per_pixel: Resolution for the top-down map.
style: Visual style configuration.
Returns:
PIL.Image.Image: Rendered top-down map with annotations.
Raises:
ValueError: If room_nodes is empty or meters_per_pixel <= 0.
NotImplementedError: If object_nodes or edges are provided.
"""
if not elements.room_nodes: if not elements.room_nodes:
raise ValueError("room_nodes must not be empty") raise ValueError("room_nodes must not be empty")
@@ -47,37 +67,33 @@ def render_topdown_scene_map(
if style is None: if style is None:
style = TopDownRenderStyle() style = TopDownRenderStyle()
if maps_module is None: # Import habitat maps internally — treated as required dependency.
maps_module = import_module("habitat.utils.visualizations.maps") from habitat.utils.visualizations import maps
# Generate raw top-down occupancy map.
map_height = float(elements.room_nodes[0].center[1]) map_height = float(elements.room_nodes[0].center[1])
top_down_map = maps_module.get_topdown_map( top_down_map = maps.get_topdown_map(
pathfinder, pathfinder,
height=map_height, height=map_height,
meters_per_pixel=meters_per_pixel, meters_per_pixel=meters_per_pixel,
) )
if not use_matplotlib: # Render with matplotlib Agg backend (no GUI required).
return top_down_map fig = Figure(figsize=style.figure_size)
_ = FigureCanvasAgg(fig)
if plt_module is None: ax = fig.add_subplot(111)
try: ax.imshow(top_down_map, cmap=style.map_cmap)
plt_module = import_module("matplotlib.pyplot")
except ImportError:
return top_down_map
plt_module.figure(figsize=style.figure_size)
plt_module.imshow(top_down_map, cmap=style.map_cmap)
# Annotate room positions and labels.
for room_node in elements.room_nodes: for room_node in elements.room_nodes:
grid_y, grid_x = maps_module.to_grid( grid_y, grid_x = maps.to_grid(
float(room_node.center[2]), float(room_node.center[2]),
float(room_node.center[0]), float(room_node.center[0]),
top_down_map.shape, top_down_map.shape,
pathfinder=pathfinder, pathfinder=pathfinder,
) )
plt_module.scatter(grid_x, grid_y, c=style.room_color, s=style.room_marker_size) ax.scatter(grid_x, grid_y, c=style.room_color, s=style.room_marker_size)
plt_module.text( ax.text(
grid_x + style.room_label_offset, grid_x + style.room_label_offset,
grid_y + style.room_label_offset, grid_y + style.room_label_offset,
room_node.room_id, room_node.room_id,
@@ -85,7 +101,12 @@ def render_topdown_scene_map(
fontsize=8, fontsize=8,
) )
plt_module.title(style.title) ax.set_title(style.title)
plt_module.axis("off") ax.axis("off")
plt_module.show() fig.canvas.draw()
return top_down_map
# Extract RGB pixels from the Agg canvas buffer.
rgba = np.asarray(fig.canvas.buffer_rgba(), dtype=np.uint8)
rgb = rgba[..., :3].copy()
return Image.fromarray(rgb)

View File

@@ -107,21 +107,18 @@ def habitat_setup(HabitatSimulatorConfig, RoomNode, create_habitat_simulator, np
def render_topdown( def render_topdown(
TopDownSceneElements, TopDownSceneElements,
meters_per_pixel, meters_per_pixel,
mo,
render_topdown_scene_map, render_topdown_scene_map,
room_nodes, room_nodes,
sim, sim,
): ):
from habitat.utils.visualizations import maps image = render_topdown_scene_map(
from matplotlib import pyplot as plt
render_topdown_scene_map(
pathfinder=sim.pathfinder, pathfinder=sim.pathfinder,
elements=TopDownSceneElements(room_nodes=room_nodes), elements=TopDownSceneElements(room_nodes=room_nodes),
meters_per_pixel=meters_per_pixel, meters_per_pixel=meters_per_pixel,
maps_module=maps,
plt_module=plt,
) )
return mo.image(image)
return (image,)
@app.cell @app.cell