blender-chat / app.py
dev-bjoern's picture
Update app.py
8037a7c verified
#!/usr/bin/env python3
import gradio as gr
import subprocess
from pathlib import Path
import torch
import os
import logging
import sys
import spaces
# Logging konfigurieren
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
class InfinigenManager:
def __init__(self, base_dir="infinigen", output_dir="outputs", blender_version="4.3.2"):
"""Initialisiert den InfinigenManager mit Basisverzeichnis, Ausgabeverzeichnis und Blender-Version."""
self.base_dir = Path(base_dir)
self.output_dir = Path(output_dir)
self.blender_version = blender_version
self.blender_bin = None
self.blender_python = None
self.initialized = False
logger.info("Initialisiere InfinigenManager...")
self._initialize()
def _run_command(self, command, error_msg="Fehler bei Befehlsausführung", timeout=None):
"""Führt Befehle einheitlich aus und behandelt Fehler."""
try:
result = subprocess.run(
command,
shell=isinstance(command, str),
capture_output=True,
text=True,
check=True,
timeout=timeout
)
logger.debug(f"STDOUT: {result.stdout}")
return result
except subprocess.CalledProcessError as e:
logger.error(f"{error_msg}: {e.stderr}")
raise
except subprocess.TimeoutExpired as e:
logger.error(f"{error_msg} (Timeout): {e.stderr}")
raise
def _initialize(self):
"""Führt die zentrale Initialisierung durch."""
try:
self._setup_blender()
self._setup_infinigen()
self._check_cuda()
self.initialized = True
logger.info("Initialisierung erfolgreich abgeschlossen.")
except Exception as e:
logger.error(f"Initialisierung fehlgeschlagen: {e}")
self.initialized = False
def _setup_blender(self):
"""Richtet Blender und dessen Python-Interpreter ein."""
logger.info("Starte Blender-Setup...")
self.blender_bin = self._find_blender() or self._install_latest_blender()
self.blender_python = self._find_blender_python_with_grep()
if not self.blender_python:
raise RuntimeError("Blender Python-Interpreter nicht gefunden!")
logger.info(f"Blender Python gefunden: {self.blender_python}")
def _find_blender(self):
"""Sucht nach einer vorhandenen Blender-Installation."""
blender_paths = [
f"/home/user/blender-{self.blender_version}-linux-x64/blender",
"/usr/bin/blender"
]
for bin_path in blender_paths:
if os.path.exists(bin_path):
try:
version_result = self._run_command([bin_path, "-v"], "Kann Blender-Version nicht prüfen", timeout=10)
version = next((line.split()[1] for line in version_result.stdout.split("\n")
if "Blender" in line and "." in line), None)
logger.info(f"Vorhandene Blender-Version: {version or 'unbekannt'}")
if version and version >= self.blender_version:
return bin_path
except Exception as e:
logger.warning(f"Fehler bei Blender-Prüfung ({bin_path}): {e}")
return None
def _install_latest_blender(self):
"""Installiert Blender 4.3.2, falls nicht vorhanden."""
logger.info("Installiere Blender 4.3.2...")
url = "https://download.blender.org/release/Blender4.3/blender-4.3.2-linux-x64.tar.xz"
tar_path = "blender-4.3.2-linux-x64.tar.xz"
blender_dir = Path(f"/home/user/blender-{self.blender_version}-linux-x64")
self._run_command(f"wget -q {url}", "Fehler beim Herunterladen von Blender", timeout=60)
self._run_command(f"tar -xvf {tar_path} -C /home/user", "Fehler beim Entpacken von Blender", timeout=60)
os.remove(tar_path)
return blender_dir / "blender"
def _find_blender_python_with_grep(self):
"""Sucht den Blender-Python-Pfad mit find."""
base_path = Path(self.blender_bin).parent.parent
grep_cmd = f"find {base_path} -type f -path '*/python/bin/python3*' -executable"
try:
result = self._run_command(grep_cmd, "Fehler bei der Python-Suche mit find")
python_paths = result.stdout.strip().split("\n")
for path in python_paths:
if path:
version_result = self._run_command([path, "--version"], "Fehler bei Python-Version", timeout=5)
logger.info(f"Python-Version: {version_result.stdout.strip()}")
return path
logger.warning("Kein passender Python-Pfad in find-Ausgabe gefunden.")
return None
except Exception as e:
logger.error(f"Fehler bei der Suche nach Blender-Python: {e}")
return None
def _check_cuda(self):
"""Prüft die CUDA-Verfügbarkeit für GPU-Nutzung."""
cuda_available = torch.cuda.is_available()
logger.info(f"CUDA {'verfügbar' if cuda_available else 'nicht verfügbar'}: {torch.cuda.get_device_name(0) if cuda_available else 'N/A'}")
def _setup_infinigen(self):
"""Richtet Infinigen ein, falls nicht installiert."""
if not self.blender_python:
logger.error("Kann Infinigen nicht installieren: Blender Python fehlt.")
return
logger.info("Starte Infinigen-Setup...")
if not self.base_dir.exists():
self._run_command(f"git clone https://github.com/princeton-vl/infinigen.git {self.base_dir}",
"Fehler beim Klonen von Infinigen", timeout=30)
# Installiere Infinigen mit Abhängigkeiten
self._run_command([
self.blender_python, "-m", "pip", "install", "-e", f"{self.base_dir}[terrain,vis]", "--user"
], "Fehler bei der Installation von Infinigen", timeout=60)
# Überprüfe, ob das datagen-Modul verfügbar ist
if not self._is_datagen_available():
raise RuntimeError("Das 'datagen'-Modul von Infinigen ist nicht verfügbar. Überprüfe die Installation.")
def _is_datagen_available(self):
"""Prüft, ob das 'datagen'-Modul von Infinigen verfügbar ist."""
try:
self._run_command([self.blender_python, "-c", "import infinigen.datagen"], timeout=10)
return True
except Exception as e:
logger.error(f"Fehler beim Import von 'infinigen.datagen': {e}")
return False
@spaces.GPU
def generate_scene(self, seed, configs=None, pipeline_configs=None):
"""Generiert eine Szene mit Infinigen auf der GPU."""
if not self.initialized or not self.blender_python:
return "Fehler: Infinigen nicht initialisiert!"
logger.info(f"Generiere Szene mit Seed: {seed}")
self.output_dir.mkdir(exist_ok=True)
configs = configs or ["infinigen_examples/configs/desert.gin", "infinigen_examples/configs/simple.gin"]
pipeline_configs = pipeline_configs or [
"infinigen_examples/configs/local_16GB.gin",
"infinigen_examples/configs/monocular.gin",
"infinigen_examples/configs/blender_gt.gin"
]
command = [
self.blender_python, "-m", "infinigen.datagen.manage_jobs",
"--output_folder", str(self.output_dir),
"--num_scenes", "1",
"--specific_seed", str(int(seed)),
"--configs"
] + configs + ["--pipeline_configs"] + pipeline_configs
try:
self._run_command(command, "Fehler bei Szenengenerierung", timeout=300)
output_path = self.output_dir / "0000000000.png"
return str(output_path) if output_path.exists() else "Fehler: Bild nicht gefunden!"
except Exception as e:
return f"Fehler bei Szenengenerierung: {str(e)}"
# Manager initialisieren
logger.info("Starte Manager-Initialisierung...")
manager = InfinigenManager()
# Gradio-Oberfläche definieren
with gr.Blocks(title="Infinigen Demo") as demo:
gr.Markdown("## Infinigen Scene Generator")
seed_input = gr.Number(label="Seed", value=0, precision=0)
output_image = gr.Image(label="Generierte Szene")
generate_button = gr.Button("Szene generieren")
generate_button.click(fn=manager.generate_scene, inputs=[seed_input], outputs=[output_image])
logger.info("Starte Gradio-Oberfläche...")
demo.launch()