mirror of
https://github.com/SikongJueluo/Mini-Nav.git
synced 2026-07-12 20:15:31 +08:00
feat(simulator): add image saving utilities for verification
This commit is contained in:
@@ -3,16 +3,19 @@ from .habitat import (
|
|||||||
close_habitat_simulator,
|
close_habitat_simulator,
|
||||||
create_habitat_simulator,
|
create_habitat_simulator,
|
||||||
)
|
)
|
||||||
|
from .image_save import save_object_image, save_room_view
|
||||||
from .topdown import TopDownRenderStyle, TopDownSceneElements, render_topdown_scene_map
|
from .topdown import TopDownRenderStyle, TopDownSceneElements, render_topdown_scene_map
|
||||||
from .views import RoomViewsByRoom, collect_room_views_by_room
|
from .views import RoomViewsByRoom, collect_room_views_by_room
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"HabitatSimulatorConfig",
|
"HabitatSimulatorConfig",
|
||||||
|
"RoomViewsByRoom",
|
||||||
"TopDownRenderStyle",
|
"TopDownRenderStyle",
|
||||||
"TopDownSceneElements",
|
"TopDownSceneElements",
|
||||||
"RoomViewsByRoom",
|
|
||||||
"close_habitat_simulator",
|
"close_habitat_simulator",
|
||||||
"collect_room_views_by_room",
|
"collect_room_views_by_room",
|
||||||
"create_habitat_simulator",
|
"create_habitat_simulator",
|
||||||
"render_topdown_scene_map",
|
"render_topdown_scene_map",
|
||||||
|
"save_object_image",
|
||||||
|
"save_room_view",
|
||||||
]
|
]
|
||||||
|
|||||||
59
mini-nav/simulator/image_save.py
Normal file
59
mini-nav/simulator/image_save.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
"""Image saving utilities for simulation verification output."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def save_room_view(
|
||||||
|
output_dir: Path,
|
||||||
|
room_id: str,
|
||||||
|
view_idx: int,
|
||||||
|
image: Image.Image,
|
||||||
|
) -> Path:
|
||||||
|
"""Save a single room view image to disk.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_dir: Base output directory.
|
||||||
|
room_id: Room identifier string.
|
||||||
|
view_idx: Zero-based view index within the room.
|
||||||
|
image: PIL Image to save.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path where the image was saved.
|
||||||
|
"""
|
||||||
|
room_dir = output_dir / room_id
|
||||||
|
room_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
path = room_dir / f"view_{view_idx:03d}.png"
|
||||||
|
image.convert("RGB").save(path, format="PNG")
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def save_object_image(
|
||||||
|
output_dir: Path,
|
||||||
|
room_id: str,
|
||||||
|
obj_id: str,
|
||||||
|
view_idx: int,
|
||||||
|
mask_idx: int,
|
||||||
|
image: Image.Image,
|
||||||
|
) -> Path:
|
||||||
|
"""Save a masked object image to disk.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_dir: Base output directory.
|
||||||
|
room_id: Room identifier string.
|
||||||
|
obj_id: Object identifier string.
|
||||||
|
view_idx: Zero-based view index.
|
||||||
|
mask_idx: Zero-based mask index within the view.
|
||||||
|
image: PIL Image to save.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path where the image was saved.
|
||||||
|
"""
|
||||||
|
room_dir = output_dir / room_id
|
||||||
|
room_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
path = room_dir / f"{obj_id}_view{view_idx:03d}_mask{mask_idx:02d}.png"
|
||||||
|
image.convert("RGB").save(path, format="PNG")
|
||||||
|
return path
|
||||||
@@ -4,6 +4,7 @@ from .feature_extractor import (
|
|||||||
extract_single_image_feature,
|
extract_single_image_feature,
|
||||||
infer_vector_dim,
|
infer_vector_dim,
|
||||||
)
|
)
|
||||||
|
from .image import numpy_to_pil
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"get_device",
|
"get_device",
|
||||||
@@ -11,4 +12,5 @@ __all__ = [
|
|||||||
"infer_vector_dim",
|
"infer_vector_dim",
|
||||||
"extract_single_image_feature",
|
"extract_single_image_feature",
|
||||||
"extract_batch_features",
|
"extract_batch_features",
|
||||||
|
"numpy_to_pil",
|
||||||
]
|
]
|
||||||
|
|||||||
21
mini-nav/utils/image.py
Normal file
21
mini-nav/utils/image.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
"""Image conversion utilities."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def numpy_to_pil(rgb: np.ndarray) -> Image.Image:
|
||||||
|
"""Convert an RGB numpy array to a PIL Image.
|
||||||
|
|
||||||
|
Handles arrays with 4 channels (RGBA) by dropping the alpha channel.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rgb: Numpy array of shape (H, W, C) with dtype uint8 or compatible.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
PIL Image in RGB mode.
|
||||||
|
"""
|
||||||
|
rgb3 = rgb[..., :3] if rgb.shape[-1] > 3 else rgb
|
||||||
|
return Image.fromarray(rgb3.astype(np.uint8))
|
||||||
@@ -23,6 +23,8 @@ def import_packages():
|
|||||||
from matplotlib import pyplot as plt
|
from matplotlib import pyplot as plt
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
from rich.progress import track
|
||||||
|
|
||||||
from configs import cfg_manager
|
from configs import cfg_manager
|
||||||
from compressors.pipeline import HashPipeline
|
from compressors.pipeline import HashPipeline
|
||||||
from scenegraph import ObjectNode, RoomNode, SimpleSceneGraph
|
from scenegraph import ObjectNode, RoomNode, SimpleSceneGraph
|
||||||
@@ -32,8 +34,11 @@ def import_packages():
|
|||||||
collect_room_views_by_room,
|
collect_room_views_by_room,
|
||||||
create_habitat_simulator,
|
create_habitat_simulator,
|
||||||
render_topdown_scene_map,
|
render_topdown_scene_map,
|
||||||
|
save_object_image,
|
||||||
|
save_room_view,
|
||||||
)
|
)
|
||||||
from compressors.proposal import extract_masked_region, generate_proposals_batch
|
from compressors.proposal import extract_masked_region, generate_proposals_batch
|
||||||
|
from utils import numpy_to_pil
|
||||||
|
|
||||||
return (
|
return (
|
||||||
HabitatSimulatorConfig,
|
HabitatSimulatorConfig,
|
||||||
@@ -50,10 +55,14 @@ def import_packages():
|
|||||||
maps,
|
maps,
|
||||||
mo,
|
mo,
|
||||||
np,
|
np,
|
||||||
|
numpy_to_pil,
|
||||||
pl,
|
pl,
|
||||||
plt,
|
plt,
|
||||||
render_topdown_scene_map,
|
render_topdown_scene_map,
|
||||||
|
save_object_image,
|
||||||
|
save_room_view,
|
||||||
generate_proposals_batch,
|
generate_proposals_batch,
|
||||||
|
track,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -146,12 +155,16 @@ def build_scene_graph_pipeline(
|
|||||||
hash_bits,
|
hash_bits,
|
||||||
mo,
|
mo,
|
||||||
np,
|
np,
|
||||||
|
numpy_to_pil,
|
||||||
pipeline_batch_size,
|
pipeline_batch_size,
|
||||||
room_nodes,
|
room_nodes,
|
||||||
sam_max_masks,
|
sam_max_masks,
|
||||||
sam_min_area,
|
sam_min_area,
|
||||||
generate_proposals_batch,
|
generate_proposals_batch,
|
||||||
|
save_object_image,
|
||||||
|
save_room_view,
|
||||||
sim,
|
sim,
|
||||||
|
track,
|
||||||
views_per_room,
|
views_per_room,
|
||||||
):
|
):
|
||||||
all_room_views = collect_room_views_by_room(
|
all_room_views = collect_room_views_by_room(
|
||||||
@@ -186,10 +199,7 @@ def build_scene_graph_pipeline(
|
|||||||
]
|
]
|
||||||
object_dataset = []
|
object_dataset = []
|
||||||
|
|
||||||
room_view_images = []
|
room_view_images = [numpy_to_pil(rgb) for _, _, rgb in room_view_dataset]
|
||||||
for _, _, rgb in room_view_dataset:
|
|
||||||
rgb3 = rgb[..., :3] if rgb.shape[-1] > 3 else rgb
|
|
||||||
room_view_images.append(Image.fromarray(rgb3.astype(np.uint8)))
|
|
||||||
|
|
||||||
masks_dataset = generate_proposals_batch(
|
masks_dataset = generate_proposals_batch(
|
||||||
hash_pipeline.mask_generator,
|
hash_pipeline.mask_generator,
|
||||||
@@ -202,18 +212,11 @@ def build_scene_graph_pipeline(
|
|||||||
raise RuntimeError("SAM dataset output size mismatch with room_view_dataset.")
|
raise RuntimeError("SAM dataset output size mismatch with room_view_dataset.")
|
||||||
|
|
||||||
dataset_jobs = list(zip(room_view_dataset, room_view_images, masks_dataset))
|
dataset_jobs = list(zip(room_view_dataset, room_view_images, masks_dataset))
|
||||||
for (room_id, view_idx, _), image, masks in mo.status.progress_bar(
|
for (room_id, view_idx, _), image, masks in track(
|
||||||
dataset_jobs,
|
dataset_jobs,
|
||||||
title="Building object dataset",
|
description="Building object dataset...",
|
||||||
subtitle="Running SAM segmentation",
|
|
||||||
show_eta=True,
|
|
||||||
show_rate=True,
|
|
||||||
):
|
):
|
||||||
room_output_dir = verification_output_dir / room_id
|
save_room_view(verification_output_dir, room_id, view_idx, image)
|
||||||
room_output_dir.mkdir(parents=True, exist_ok=True)
|
|
||||||
room_view_path = room_output_dir / f"view_{view_idx:03d}.png"
|
|
||||||
image.convert("RGB").save(room_view_path, format="PNG")
|
|
||||||
|
|
||||||
total_masks += len(masks)
|
total_masks += len(masks)
|
||||||
|
|
||||||
for mask_idx, mask in enumerate(masks):
|
for mask_idx, mask in enumerate(masks):
|
||||||
@@ -252,12 +255,9 @@ def build_scene_graph_pipeline(
|
|||||||
obj_id = f"obj_{object_index:04d}"
|
obj_id = f"obj_{object_index:04d}"
|
||||||
object_index += 1
|
object_index += 1
|
||||||
|
|
||||||
room_output_dir = verification_output_dir / room_id
|
save_object_image(
|
||||||
room_output_dir.mkdir(parents=True, exist_ok=True)
|
verification_output_dir, room_id, obj_id, view_idx, mask_idx, masked_image
|
||||||
object_image_path = (
|
|
||||||
room_output_dir / f"{obj_id}_view{view_idx:03d}_mask{mask_idx:02d}.png"
|
|
||||||
)
|
)
|
||||||
masked_image.convert("RGB").save(object_image_path, format="PNG")
|
|
||||||
|
|
||||||
bits_array = np.asarray(bits.detach().cpu().numpy()).reshape(-1)
|
bits_array = np.asarray(bits.detach().cpu().numpy()).reshape(-1)
|
||||||
if bits_array.size == 512:
|
if bits_array.size == 512:
|
||||||
|
|||||||
Reference in New Issue
Block a user