Files
appRobotRender/setup/generateSets/render_Loop.py
2026-06-02 11:37:29 +02:00

202 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import os
import subprocess
import shutil
from pathlib import Path
import argparse
STATE_KEYS = ("x", "y", "z", "a", "b", "c", "e")
def update_robot_json(robot_json_file, camera_position, camera_target, default_position, width, height, fstop):
"""Aktualisiert cameraPosition/Target, Auflösung und defaultPosition in robot.json.
Hinweis: render_robot.py liest robot.json bei jedem Blender-Start frisch ein,
deshalb wird hier pro Kamera in die Datei geschrieben.
"""
try:
with open(robot_json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
data['renderingInfo']['cameraPosition'] = camera_position
data['renderingInfo']['cameraTarget'] = camera_target
data['renderingInfo']['width'] = width
data['renderingInfo']['height'] = height
data['renderingInfo']['dofFStop'] = fstop
data['defaultPosition'] = default_position
with open(robot_json_file, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2)
except FileNotFoundError:
print(f"Fehler: Datei {robot_json_file} nicht gefunden.")
return False
except json.JSONDecodeError:
print(f"Fehler: JSON-Datei {robot_json_file} ist ungültig.")
return False
return True
def run_blender(blender_executable, script_path, log_level):
"""Führt Blender mit dem angegebenen Skript aus."""
try:
command = [
blender_executable,
"-b",
"--python",
script_path,
"--log-level",
str(log_level)
]
subprocess.run(command, check=True, capture_output=True, text=True)
return True
except subprocess.CalledProcessError as e:
print(f"Blender-Skript fehlgeschlagen:\n{e.stderr}")
return False
def copy_and_rename_file(source_file, destination_dir, new_filename):
"""Kopiert die erstellte Datei in den Zielordner und benennt sie um."""
destination_path = os.path.join(destination_dir, new_filename)
try:
shutil.copy2(source_file, destination_path) # copy2 behält Metadaten
return True
except FileNotFoundError:
print(f"Fehler: Quelldatei {source_file} nicht gefunden.")
return False
except Exception as e:
print(f"Fehler beim Kopieren/Umbenennen der Datei: {e}")
return False
def load_render_config(robot_json_file):
"""Liest Posen, Kamera-Setup und Render-Defaults aus robot.json."""
with open(robot_json_file, 'r', encoding='utf-8') as f:
data = json.load(f)
poses = data.get("robot_test_poses", {}) or {}
cam_positions = data.get("test_camera_positions", {}) or {}
cam_targets = data.get("test_camera_targets", {}) or {}
rinfo = data.get("renderingInfo", {}) or {}
default_rendering = {
"width": int(rinfo.get("width", 1280)),
"height": int(rinfo.get("height", 720)),
"dofFStop": float(rinfo.get("dofFStop", 11)),
}
return poses, cam_positions, cam_targets, default_rendering
def split_pose(pose_entry):
"""Trennt eine robot_test_poses-Definition in (joint_state, rendering_override)."""
joint_state = {k: pose_entry[k] for k in STATE_KEYS if k in pose_entry}
rendering_override = pose_entry.get("rendering", {}) or {}
return joint_state, rendering_override
def main():
USER_HOME = Path.home()
BASE = Path(__file__).resolve().parents[2]
BLENDER_EXE = str("C:/Program Files/Blender Foundation/Blender 4.5/blender.exe")
ROBOT_JSON_FILE = str(BASE / "data" / "robot" / "robot.json")
OUTPUT_DIR = str(BASE / "data" / "simulation" / "debug")
RENDER_PY = str(BASE / "setup" / "generateSets" / "render_robot.py")
RENDER_PNG = str(BASE / "data" / "simulation" / "debug" / "render.png")
OUTPUT_SET = str(BASE / "data" / "simulation")
parser = argparse.ArgumentParser(description="Automatisiert die Roboter-Rendering-Pipeline.")
parser.add_argument("robot_json", nargs="?", default=ROBOT_JSON_FILE, help="Pfad zur robot.json-Datei.")
parser.add_argument("blender_executable", nargs="?", default=BLENDER_EXE, help="Pfad zur Blender-Executable.")
parser.add_argument("render_script", nargs="?", default=RENDER_PY, help="Pfad zum render_robot.py-Skript.")
parser.add_argument("render_png", nargs="?", default=RENDER_PNG, help="vom Skript erzeugte png")
parser.add_argument("output_dir", nargs="?", default=OUTPUT_DIR, help="Zielordner für die gerenderten Blender Bilder.")
parser.add_argument("output_set", nargs="?", default=OUTPUT_SET, help="Zielordner in dem die Sets abgelegt werden")
parser.add_argument("--poses", nargs="*", default=None,
help="Nur diese Pose-Keys rendern (z.B. --poses 8 9). Default: alle aus robot_test_poses.")
parser.add_argument("--scene_prefix", default="Scene", help="Präfix für die Scene-Ordnernamen (Default: Scene).")
parser.add_argument("--log_level", type=int, default=2, help="Log-Level für Blender (Standard: 2).")
args = parser.parse_args()
# Konfiguration aus robot.json
robot_poses, camera_positions, camera_targets, default_rendering = load_render_config(args.robot_json)
if not robot_poses:
print("Keine 'robot_test_poses' in robot.json gefunden.")
return
if not camera_positions:
print("Keine 'test_camera_positions' in robot.json gefunden.")
return
# Optionaler Pose-Filter
if args.poses:
missing = [p for p in args.poses if p not in robot_poses]
for p in missing:
print(f"[WARN] Pose '{p}' nicht in robot_test_poses wird übersprungen.")
selected = {p: robot_poses[p] for p in args.poses if p in robot_poses}
else:
selected = robot_poses
print(f"Zu rendernde Posen: {list(selected.keys())}")
print(f"Kameras pro Pose: {list(camera_positions.keys())}")
for pose_name, pose_entry in selected.items():
joint_state, rendering_override = split_pose(pose_entry)
# Render-Settings: globaler Default, pro Pose überschreibbar
rendering = dict(default_rendering)
rendering.update({k: rendering_override[k] for k in ("width", "height", "dofFStop") if k in rendering_override})
new_folder = Path(args.output_set) / f"{args.scene_prefix}{pose_name}"
os.makedirs(new_folder, exist_ok=True)
print(f"\n=== Scene {pose_name} | {rendering['width']}x{rendering['height']} f/{rendering['dofFStop']} ===")
# Ground-Truth-Record: Joint-Pose + Kamera-Setup (für spätere Messung/GT-Posen)
pose_file = new_folder / "pose.json"
pose_data = {
"name": pose_name,
"position": joint_state,
"rendering": rendering,
"camera_positions": camera_positions,
"camera_targets": camera_targets,
}
with open(pose_file, "w", encoding="utf-8") as f:
json.dump(pose_data, f, indent=2)
for frame_name, camera_position in camera_positions.items():
camera_target = camera_targets.get(frame_name)
if camera_target is None:
print(f"[WARN] Kein Target für Kamera '{frame_name}' übersprungen.")
continue
# 1. robot.json für diesen Render aktualisieren
if not update_robot_json(args.robot_json, camera_position, camera_target, joint_state,
rendering["width"], rendering["height"], rendering["dofFStop"]):
continue
# 2. Blender ausführen
if not run_blender(args.blender_executable, args.render_script, args.log_level):
continue
# 3. Ergebnisse kopieren und umbenennen (png, npz, markers.json)
new_filename = f"render_{frame_name}.png"
if not copy_and_rename_file(args.render_png, new_folder, new_filename):
continue
if not copy_and_rename_file(args.render_png.replace(".png", ".npz"),
new_folder, new_filename.replace(".png", ".npz")):
continue
if not copy_and_rename_file(args.render_png.replace("render.png", "markers.json"),
new_folder, new_filename.replace(".png", ".json")):
continue
print(f" Frame {frame_name}: OK")
print(f"Scene {pose_name} fertig -> {new_folder}")
if __name__ == "__main__":
main()