TeddyBearKinova / bundle /sim /build_scene.py
lsnu's picture
Upload folder using huggingface_hub
d93804e verified
from __future__ import annotations
import argparse
from pathlib import Path
import cv2
import numpy as np
import pandas as pd
from PIL import Image
from scene_tools import (
create_background_inpaint,
load_scene_calibration,
render_scene,
save_scene_json,
)
def _save_mask_overlay(rgb: np.ndarray, mask: np.ndarray, color: tuple[int, int, int], path: Path) -> None:
overlay = rgb.copy()
overlay[mask] = color
Image.fromarray(overlay).save(path)
def _save_box_outline(rgb: np.ndarray, polygon_world: np.ndarray, calibration, path: Path) -> None:
canvas = rgb.copy()
image_points, _ = cv2.projectPoints(
polygon_world.astype(np.float64), calibration.rvec, calibration.tvec, calibration.camera_matrix, None
)
pts = np.round(image_points[:, 0, :]).astype(np.int32)
cv2.polylines(canvas, [pts], True, (255, 0, 0), 4)
Image.fromarray(canvas).save(path)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument(
"--session-root",
default="/workspace/data/teddybear_raw/session_20260327_165944_bear",
help="Raw session root used for calibration.",
)
parser.add_argument(
"--sync-row-index",
type=int,
default=0,
help="Row of sync_index.csv to use as the start frame.",
)
parser.add_argument(
"--output-dir",
default="/workspace/kinova_scene_sim/outputs",
help="Directory for the scene JSON and visual checks.",
)
args = parser.parse_args()
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
calibration = load_scene_calibration(args.session_root, args.sync_row_index)
sync = pd.read_csv(Path(args.session_root) / "sync_index.csv")
start_q = sync.iloc[args.sync_row_index][[f"q{i}_deg" for i in range(1, 8)]].to_numpy(np.float64)
motion_q = sync.iloc[[0, 56, 70, 84, 98, 112]][[f"q{i}_deg" for i in range(1, 8)]].to_numpy(np.float64)
background = create_background_inpaint(calibration.rgb)
scene_json = output_dir / f"{Path(args.session_root).name}_scene_fit.json"
save_scene_json(calibration, scene_json)
Image.fromarray(calibration.rgb).save(output_dir / "real_start.png")
Image.fromarray(background).save(output_dir / "background_inpaint.png")
_save_mask_overlay(calibration.rgb, calibration.box_mask, (255, 0, 0), output_dir / "box_mask_overlay.png")
_save_mask_overlay(calibration.rgb, calibration.teddy_mask, (0, 255, 0), output_dir / "teddy_mask_overlay.png")
_save_box_outline(calibration.rgb, calibration.box_world_polygon, calibration, output_dir / "box_world_outline.png")
start_render = render_scene(calibration, start_q, background=background)
Image.fromarray(start_render).save(output_dir / "scene_start_render.png")
colors = [
(255, 64, 64),
(64, 255, 64),
(64, 64, 255),
(255, 255, 64),
(255, 64, 255),
(64, 255, 255),
]
motion_render = background.copy()
for q_deg, color in zip(motion_q, colors):
motion_render = render_scene(calibration, q_deg, background=motion_render, color=color, alpha=0.95)
Image.fromarray(motion_render).save(output_dir / "scene_motion_fit.png")
side_by_side = np.concatenate([calibration.rgb, start_render], axis=1)
Image.fromarray(side_by_side).save(output_dir / "real_vs_scene_start.png")
print(f"saved scene json to {scene_json}")
print(f"start frame: azure_rgb/{calibration.azure_rgb_seq:06d}.jpg")
print(f"depth frame: azure_depth/{calibration.azure_depth_seq:06d}.png")
print(f"robot seq: {calibration.robot_seq}")
print(f"box height estimate: {calibration.box_height_m:.3f} m")
print(f"teddy height estimate: {calibration.teddy_height_m:.3f} m")
print(f"teddy center world: {calibration.teddy_world_center.tolist()}")
if __name__ == "__main__":
main()