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