Spaces:
Paused
Paused
File size: 22,364 Bytes
f187247 bec2965 64f982b f187247 8e335ab f187247 bec2965 fcebf9d 64f982b fcebf9d 64f982b fcebf9d 64f982b fcebf9d 64f982b fcebf9d 64f982b f187247 f1e112b f187247 f1e112b f187247 64f982b bec2965 64f982b fcebf9d 64f982b fcebf9d 64f982b fcebf9d 64f982b fcebf9d 64f982b f187247 fcebf9d f187247 fcebf9d f187247 fcebf9d f187247 18bcb1f fcebf9d f187247 fcebf9d f187247 fcebf9d f187247 18bcb1f f187247 18bcb1f f187247 9caea3a f187247 e5385f9 9caea3a f5175a7 9caea3a 6f98a55 9caea3a 6f98a55 9caea3a 6f98a55 9caea3a f187247 fcebf9d f187247 fcebf9d f187247 f1e112b f187247 f1e112b f187247 f1e112b f187247 f1e112b f187247 f1e112b f187247 25d2ec9 64f982b 25d2ec9 f5175a7 25d2ec9 f5175a7 fcebf9d 25d2ec9 f5175a7 25d2ec9 fcebf9d 25d2ec9 f5175a7 25d2ec9 f187247 25d2ec9 f187247 64f982b f187247 25d2ec9 f187247 f1e112b f187247 f1e112b f187247 25d2ec9 f1e112b 25d2ec9 f187247 f1e112b f187247 25d2ec9 f1e112b f187247 f1e112b f187247 64f982b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 |
import argparse
import subprocess
import tempfile
from pathlib import Path
import sys
import os
import platform
import shutil
import urllib.request
import zipfile
from typing import List
root_dir = Path(__file__).resolve().parents[2]
sys.path.append(str(root_dir))
def _download_import_ldraw(target_root: Path) -> Path | None:
"""
Download ImportLDraw from GitHub releases if it wasn't bundled in the repo.
Returns the extracted directory or None if download failed.
"""
default_url = (
"https://github.com/TobyLobster/ImportLDraw/archive/refs/tags/v1.2.1.zip"
)
download_url = os.environ.get("IMPORT_LDRAW_DOWNLOAD_URL", default_url)
target_dir = target_root / "ImportLDraw_auto"
print("⚠️ 未在项目中找到 ImportLDraw,尝试从 GitHub 下载...")
print(f"🌐 下载地址: {download_url}")
try:
with tempfile.TemporaryDirectory(prefix="importldraw_dl_") as tmpdir:
tmpdir_path = Path(tmpdir)
zip_path = tmpdir_path / "importldraw.zip"
urllib.request.urlretrieve(download_url, zip_path)
print("✅ 已下载 ImportLDraw 压缩包")
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(tmpdir_path)
extracted_root = None
for child in tmpdir_path.iterdir():
if child.is_dir() and (child / "__init__.py").exists():
extracted_root = child
break
if child.is_dir():
potential = child / "ImportLDraw"
if (potential / "__init__.py").exists():
extracted_root = potential
break
if not extracted_root:
print("❌ 无法在压缩包中找到 ImportLDraw 目录")
return None
if target_dir.exists():
shutil.rmtree(target_dir)
shutil.copytree(extracted_root, target_dir)
print(f"✅ ImportLDraw 已下载到: {target_dir}")
return target_dir
except Exception as download_error:
print(f"❌ ImportLDraw 下载失败: {download_error}")
return None
def _resolve_import_ldraw_dir() -> Path:
"""
Locate ImportLDraw plugin directory (simplified for Docker deployment).
Checks only 3 deterministic locations:
1. Environment variable IMPORT_LDRAW_DIR (for local dev override)
2. {repo}/code/ImportLDraw (Docker deployment path)
3. {repo}/code/ImportLDraw_auto (auto-downloaded fallback)
Raises FileNotFoundError if not found in any location.
"""
code_dir = Path(__file__).resolve().parents[2]
# 1. Environment variable override (local development)
env_override = os.environ.get("IMPORT_LDRAW_DIR")
if env_override:
path = Path(env_override).expanduser()
if (path / "__init__.py").exists():
print(f"✓ Using IMPORT_LDRAW_DIR: {path}")
return path
else:
print(f"⚠️ IMPORT_LDRAW_DIR set but invalid: {path}")
# 2. Docker deployment path / Repository bundled path
for subdir in ["ImportLDraw", "ImportLDraw_auto"]:
path = code_dir / subdir
if (path / "__init__.py").exists():
print(f"✓ Found ImportLDraw at: {path}")
return path
# Not found - fail fast with clear error
raise FileNotFoundError(
f"ImportLDraw not found in:\n"
f" - {code_dir / 'ImportLDraw'}\n"
f" - {code_dir / 'ImportLDraw_auto'}\n"
f"Set IMPORT_LDRAW_DIR environment variable or ensure Docker build succeeded."
)
# 根据操作系统自动检测Blender路径
def _get_blender_path():
"""自动检测Blender路径(支持Mac/Linux)"""
system = platform.system()
# Mac系统常见的Blender安装路径
if system == 'Darwin':
possible_paths = [
'/Applications/Blender.app/Contents/MacOS/Blender',
'/Applications/Blender 3.6/Blender.app/Contents/MacOS/Blender',
'/Applications/Blender 4.0/Blender.app/Contents/MacOS/Blender',
os.path.expanduser('~/Applications/Blender.app/Contents/MacOS/Blender'),
]
for path in possible_paths:
if os.path.exists(path):
print(f"✓ 找到Blender: {path}")
return path
# 如果没找到,提示用户安装
print("⚠️ 未找到Blender!")
print("请从以下网址下载并安装Blender 3.6 LTS:")
print("https://www.blender.org/download/lts/3-6/")
print("安装到 /Applications/ 目录后重新运行。")
return None
# Linux系统(优先检测系统安装的Blender)
else:
# 首先检查通过apt/系统包管理器安装的Blender
system_paths = [
'/usr/bin/blender',
'/usr/local/bin/blender',
'/snap/bin/blender',
]
for path in system_paths:
if os.path.exists(path):
print(f"✓ 找到系统Blender: {path}")
return path
# 检查手动下载的版本
linux_path = '/tmp/blender-3.6.5-linux-x64/blender'
if os.path.exists(linux_path):
print(f"✓ 找到Blender: {linux_path}")
return linux_path
# 在Hugging Face Spaces等环境中,Blender应该通过packages.txt安装
# 因此不需要自动下载安装,直接返回系统路径
print("⚠️ 未找到Blender,尝试使用系统默认路径...")
return '/usr/bin/blender' # 假设通过packages.txt安装到系统路径
BLENDER_PATH = _get_blender_path()
if BLENDER_PATH and os.path.exists(BLENDER_PATH):
# 验证Blender版本
try:
version_check = subprocess.run(
[BLENDER_PATH, '--version'],
capture_output=True,
text=True,
timeout=10
)
if version_check.returncode == 0:
version_info = version_check.stdout.strip().split('\n')[0]
print(f"✓ Blender版本: {version_info}")
except Exception as e:
print(f"⚠️ Blender版本检查失败: {e}")
def render_bricks_safe(
in_file: str,
out_file: str,
reposition_camera: bool = True,
square_image: bool = True,
instructions_look: bool = False,
fov: float = 45,
img_resolution: int = 512,
) -> None:
print("=" * 70)
print("🔍 [DEBUG] render_bricks_safe() 函数被调用")
print(f" 📁 输入文件: {in_file}")
print(f" 📁 输出文件: {out_file}")
print(f" 🔧 BLENDER_PATH (全局): {BLENDER_PATH}")
print(f" ✓ Blender 是否存在: {BLENDER_PATH and os.path.exists(BLENDER_PATH) if BLENDER_PATH else False}")
print("=" * 70)
in_file = os.path.abspath(in_file)
out_file = os.path.abspath(out_file)
# 确保输出目录存在
out_dir = os.path.dirname(out_file)
Path(out_dir).mkdir(parents=True, exist_ok=True)
os.chmod(out_dir, 0o755)
ldraw_lib_path = os.environ.get('LDRAW_LIBRARY_PATH')
if not ldraw_lib_path or not os.path.exists(ldraw_lib_path):
ldraw_lib_path = Path.home() / 'ldraw'
ldraw_lib_path = os.path.abspath(ldraw_lib_path)
print(f"📦 LDraw库路径: {ldraw_lib_path}")
print(f"✓ LDraw库是否存在: {os.path.exists(ldraw_lib_path)}")
if os.path.exists(ldraw_lib_path):
parts_dir = os.path.join(ldraw_lib_path, 'parts')
print(f" - parts 目录存在: {os.path.exists(parts_dir)}")
else:
print(f" ⚠️ 警告:LDraw 库目录不存在!")
import_ldraw_dir = _resolve_import_ldraw_dir()
if not import_ldraw_dir:
autogenerated_dir = _download_import_ldraw(root_dir)
if autogenerated_dir and (autogenerated_dir / "__init__.py").exists():
import_ldraw_dir = autogenerated_dir
if not import_ldraw_dir:
raise FileNotFoundError(
"未能找到 ImportLDraw 插件目录。请确保 code/ImportLDraw "
"已包含在部署包中,或设置环境变量 IMPORT_LDRAW_DIR 指向插件位置。"
)
import_ldraw_dir = import_ldraw_dir.resolve()
import_ldraw_parent = import_ldraw_dir.parent
# Simplified: Single deterministic path for sys.path
import_ldraw_path = str(import_ldraw_parent)
print(f"🔌 ImportLDraw目录: {import_ldraw_dir}")
print(f"📍 ImportLDraw父目录: {import_ldraw_path}")
# Set PYTHONPATH for Blender (will work with --python-use-system-env flag)
blender_env = os.environ.copy()
blender_env["PYTHONPATH"] = import_ldraw_path
print(f"✓ PYTHONPATH 设置为: {import_ldraw_path}")
# 修复布尔值格式化问题
blender_script = '''
import bpy
import math
import os
import sys
from pathlib import Path
import traceback
print("=== Blender脚本开始执行 ===")
# Simplified: Single deterministic sys.path (demo.zip style)
sys.path.append("{import_ldraw_path}")
print(f"✓ Added to sys.path: {{sys.path[-1]}}")
# Import ImportLDraw (fail fast if not found)
try:
print("Importing ImportLDraw...")
import ImportLDraw
from ImportLDraw.loadldraw.loadldraw import Options, Configure, loadFromFile, FileSystem
print(f"✓ ImportLDraw loaded from: {{ImportLDraw.__file__}}")
except ImportError as import_error:
print(f"❌ ImportLDraw import failed: {{import_error}}")
print(f" sys.path: {{sys.path}}")
traceback.print_exc()
sys.exit(1)
try:
plugin_path = Path(ImportLDraw.__file__).parent
print(f"插件路径: {{plugin_path}}")
print("Blender内部输出路径: " + "{output_file}")
print("输出目录是否存在: " + str(os.path.exists(os.path.dirname("{output_file}"))))
# 检查输入文件
print(f"输入文件路径: {{'{input_file}'}}")
print(f"输入文件是否存在: {{os.path.exists('{input_file}')}}")
if os.path.exists("{input_file}"):
with open("{input_file}", "r") as f:
content = f.read()
print(f"LDR文件内容长度: {{len(content)}}")
print("文件前几行:")
for i, line in enumerate(content.split('\\n')[:5]):
print(f" {{i+1}}: {{line}}")
# 设置 ImportLDraw 选项(demo.zip 原始实现)
print("设置 ImportLDraw Options...")
Options.ldrawDirectory = "{ldraw_path}"
Options.instructionsLook = {instructions_look_bool}
Options.useLogoStuds = True
Options.useUnofficialParts = True
Options.gaps = True
Options.studLogoDirectory = os.path.join(str(plugin_path), 'studs')
Options.LSynthDirectory = os.path.join(str(plugin_path), 'lsynth')
Options.verbose = 1
Options.overwriteExistingMaterials = True
Options.overwriteExistingMeshes = True
Options.scale = 0.01
Options.createInstances = True
Options.removeDoubles = True
Options.positionObjectOnGroundAtOrigin = True
Options.flattenHierarchy = False
Options.edgeSplit = True
Options.addBevelModifier = True
Options.bevelWidth = 0.5
Options.addEnvironmentTexture = True
Options.scriptDirectory = os.path.join(str(plugin_path), 'loadldraw')
Options.addWorldEnvironmentTexture = True
Options.addGroundPlane = True
Options.setRenderSettings = True
Options.removeDefaultObjects = True
Options.positionCamera = {reposition_camera_bool}
Options.cameraBorderPercent = 0.05
print("✅ Options 设置完成")
print("执行 Configure()...")
Configure()
print("✅ Configure 执行成功")
print("清理场景...")
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
print("场景清理完成")
print("尝试加载 LDR 文件(使用 loadFromFile)...")
ldr_file = FileSystem.locate("{input_file}")
print(f"找到 LDR 文件: {{ldr_file}}")
loadFromFile(None, ldr_file)
print("✅ LDR 文件加载成功")
# 检查加载的对象
mesh_objects = [obj for obj in bpy.data.objects if obj.type == 'MESH']
print(f"加载的网格对象数量: {{len(mesh_objects)}}")
if len(mesh_objects) == 0:
print("警告: 没有加载任何网格对象!")
else:
for obj in mesh_objects[:3]: # 只显示前3个对象
print(f" 对象: {{obj.name}}, 顶点数: {{len(obj.data.vertices) if obj.data else 0}}")
scene = bpy.context.scene
if {square_image_bool}: # 直接使用布尔值
scene.render.resolution_x = {img_resolution}
scene.render.resolution_y = {img_resolution}
if scene.camera:
scene.camera.data.angle = math.radians({fov})
else:
print("警告:场景中没有相机,创建默认相机")
bpy.ops.object.camera_add(location=(5, -5, 3))
scene.camera = bpy.context.active_object
scene.render.engine = 'CYCLES'
scene.cycles.samples = 512 # 与 demo.zip 一致的高质量渲染
scene.cycles.device = 'GPU'
scene.render.image_settings.file_format = 'PNG'
scene.render.filepath = "{output_file}"
scene.render.use_overwrite = True
# Disable denoising (HF Spaces apt-get Blender lacks OpenImageDenoise)
bpy.context.view_layer.cycles.use_denoising = False
print("ℹ️ Denoising disabled (apt-get Blender has no OpenImageDenoise support)")
# GPU 配置(与 demo.zip 一致)- 优先使用 GPU,失败则自动回退到 CPU
if sys.platform == 'darwin':
compute_device_type = 'METAL'
else:
compute_device_type = 'CUDA'
print("🔧 配置 GPU...")
try:
bpy.context.preferences.addons['cycles'].preferences.compute_device_type = compute_device_type
bpy.context.preferences.addons['cycles'].preferences.get_devices()
gpu_used = False
for device in bpy.context.preferences.addons['cycles'].preferences.devices:
if device['name'].startswith(('NVIDIA', 'Apple', 'AMD')):
device['use'] = 1
gpu_used = True
print("✅ 启用 GPU: " + device['name'])
else:
device['use'] = 0
if not gpu_used:
print("⚠️ 未找到可用 GPU,将回退到 CPU 渲染")
scene.cycles.device = 'CPU'
print("ℹ️ 使用 CPU 渲染(samples=512)")
else:
print("ℹ️ 使用 GPU 渲染(" + compute_device_type + ", samples=512)")
except Exception as gpu_error:
print("⚠️ GPU 初始化失败: " + str(gpu_error))
print("ℹ️ 回退到 CPU 渲染(samples=512)")
scene.cycles.device = 'CPU'
print("开始渲染...")
result = bpy.ops.render.render(write_still=True)
print("渲染执行结果: " + str(result))
print("渲染后文件是否存在: " + str(os.path.exists("{output_file}")))
if os.path.exists("{output_file}"):
file_size = os.path.getsize("{output_file}")
print("文件生成成功: " + str(file_size) + " 字节")
else:
print("严重错误:渲染完成但未生成文件")
print("=== Blender脚本执行完成 ===")
except Exception as e:
print(f"脚本执行过程中发生错误: {{e}}")
traceback.print_exc()
sys.exit(1)
'''
# 写入临时脚本 - 修复布尔值格式化
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
script_content = blender_script.format(
import_ldraw_path=import_ldraw_path, # Simplified: single path
ldraw_path=ldraw_lib_path,
instructions_look_bool=str(instructions_look),
reposition_camera_bool=str(reposition_camera),
square_image_bool=str(square_image),
fov=fov,
img_resolution=img_resolution,
input_file=in_file,
output_file=out_file
)
f.write(script_content)
script_path = f.name
try:
# 检查Blender路径是否存在
print(f"🔍 [DEBUG] 检查 Blender 路径...")
print(f" BLENDER_PATH 值: {BLENDER_PATH}")
print(f" 类型: {type(BLENDER_PATH)}")
print(f" 是否为 None: {BLENDER_PATH is None}")
if not BLENDER_PATH or not os.path.exists(BLENDER_PATH):
print(f"❌ Blender 不可用")
print(f" - BLENDER_PATH: {BLENDER_PATH}")
print(f" - 文件存在: {os.path.exists(BLENDER_PATH) if BLENDER_PATH else 'N/A'}")
print("⚠️ 跳过渲染:未找到Blender")
print("如需3D渲染功能,请安装Blender 3.6+")
print("📝 正在生成占位图片...")
# 创建一个占位图片,避免程序崩溃
from PIL import Image, ImageDraw, ImageFont
img = Image.new('RGB', (512, 512), color=(240, 240, 240))
draw = ImageDraw.Draw(img)
draw.text((150, 250), "Blender未安装\n渲染功能不可用", fill=(100, 100, 100))
img.save(out_file)
print(f"✅ 已生成占位图片: {out_file}")
return
print(f"✅ Blender 可用: {BLENDER_PATH}")
# 首先测试 Blender 是否能正常运行
test_cmd = [BLENDER_PATH, '--version']
print(f"测试Blender: {' '.join(test_cmd)}")
try:
test_result = subprocess.run(
test_cmd,
capture_output=True,
text=True,
timeout=10,
env=blender_env
)
print(f"Blender测试返回码: {test_result.returncode}")
if test_result.stdout:
print(f"Blender版本信息: {test_result.stdout.strip()}")
if test_result.stderr:
print(f"Blender测试错误: {test_result.stderr.strip()}")
except Exception as test_e:
print(f"Blender测试失败: {test_e}")
# 在 Linux 上使用 xvfb-run 以支持无头渲染(含 GLX 扩展)
if platform.system() == 'Linux' and os.path.exists('/usr/bin/xvfb-run'):
cmd = [
'xvfb-run',
'-a', # 自动选择显示编号
'--server-args', '-screen 0 1024x768x24 -ac +extension GLX +extension RENDER +render -noreset',
BLENDER_PATH,
'--background',
'--python-use-system-env', # CRITICAL: Enable PYTHONPATH support
'--python', script_path
]
print("✅ 使用 xvfb-run 进行无头渲染(GLX 扩展已启用)")
else:
cmd = [
BLENDER_PATH,
'--background',
'--python-use-system-env', # CRITICAL: Enable PYTHONPATH support
'--python', script_path
]
print("⚠️ 直接运行 Blender(无 xvfb)")
print(f"执行命令: {' '.join(cmd)}")
print(f"脚本路径: {script_path}")
print(f"脚本是否存在: {os.path.exists(script_path)}")
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=300,
env=blender_env
)
# 输出Blender日志
print("=" * 70)
print(f"🎬 Blender 执行完成")
print("=" * 70)
print(f"返回码: {result.returncode}")
print(f"标准输出长度: {len(result.stdout) if result.stdout else 0} 字符")
print(f"错误输出长度: {len(result.stderr) if result.stderr else 0} 字符")
if result.stdout:
print("=" * 70)
print("📝 Blender 标准输出:")
print("=" * 70)
print(result.stdout)
else:
print("(标准输出为空)")
if result.stderr:
print("=" * 70)
print("❌ Blender 错误输出:")
print("=" * 70)
print(result.stderr)
else:
print("(错误输出为空)")
print("=" * 70)
if result.returncode != 0:
# 在抛出异常前,尝试解析错误信息
error_summary = "未知错误"
if result.stderr:
# 提取关键错误信息
lines = result.stderr.strip().split('\n')
for line in lines:
if 'Error' in line or 'error' in line:
error_summary = line.strip()
break
raise RuntimeError(
f"Blender返回错误码: {result.returncode}\n"
f"错误摘要: {error_summary}\n"
f"完整输出已打印在上方"
)
if not os.path.exists(out_file):
raise FileNotFoundError(f"渲染失败:未生成输出文件 {out_file}")
# 验证文件大小
file_size = os.path.getsize(out_file)
if file_size < 100:
raise RuntimeError(f"生成的文件无效(大小:{file_size}字节)")
print(f"渲染成功!文件大小: {file_size} 字节")
except subprocess.TimeoutExpired:
raise TimeoutError("渲染超时(超过5分钟)")
except Exception as e:
raise e
finally:
if os.path.exists(script_path):
os.unlink(script_path)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--in_file', type=str, required=True, help='Path to LDR file')
parser.add_argument('--out_file', type=str, required=True, help='Path to output image file')
args = parser.parse_args()
# 检查输入文件是否存在
if not os.path.exists(args.in_file):
print(f"错误: 输入文件不存在: {args.in_file}")
return
# 获取输入文件的绝对路径
in_file = os.path.abspath(args.in_file)
out_file = os.path.abspath(args.out_file)
print(f"输入文件: {in_file}")
print(f"输出文件: {out_file}")
try:
render_bricks_safe(in_file, out_file, square_image=True, instructions_look=False)
print(f'成功渲染图像到: {out_file}')
except Exception as e:
print(f'渲染失败: {e}')
if __name__ == '__main__':
main()
|