Download pipelines/get_blend_bounds.py from ManfredC/cmevs-code: direct link, hf CLI and curl.
- Browser
- Download file 6.85 kB
-
https://huggingface.co/ManfredC/cmevs-code/resolve/main/pipelines/get_blend_bounds.py
- Command line
-
hf download hf://ManfredC/cmevs-code/pipelines/get_blend_bounds.py
-
curl -L -o get_blend_bounds.py https://huggingface.co/ManfredC/cmevs-code/resolve/main/pipelines/get_blend_bounds.py
6.85 kB
| #!/usr/bin/env python3 | |
| """ | |
| 获取.blend文件的场景信息(相机位置、边界框) | |
| 此脚本在Blender内部执行,用于获取.blend文件的场景信息。 | |
| 输出JSON格式的数据供batch_render.py使用。 | |
| 智能检测策略: | |
| 1. 如果场景中有相机,使用相机位置作为第一帧参考 | |
| 2. 如果没有相机,使用室内对象(Floor、Wall、Room等)的边界框计算中心 | |
| 使用方法: | |
| blender --background --python get_blend_bounds.py -- \ | |
| --blend "path/to/scene.blend" | |
| 输出格式: | |
| [BOUNDS] {"bounds_min": [...], "bounds_max": [...], "center": [...], "camera": {...}} | |
| """ | |
| import bpy | |
| import sys | |
| import json | |
| import argparse | |
| from mathutils import Vector | |
| # 室内对象关键词 | |
| INDOOR_KEYWORDS = [ | |
| 'room', 'interior', 'floor', 'wall', 'ceiling', 'indoor', | |
| 'kitchen', 'bathroom', 'bedroom', 'living', 'dining', | |
| 'door', 'window', 'lamp', 'light', 'sofa', 'table', 'chair', | |
| 'bed', 'desk', 'cabinet', 'shelf', 'carpet', 'curtain', | |
| 'stair', 'corridor', 'hallway', 'closet', 'wardrobe' | |
| ] | |
| def parse_args(): | |
| """解析命令行参数""" | |
| argv = sys.argv | |
| if "--" in argv: | |
| argv = argv[argv.index("--") + 1:] | |
| else: | |
| argv = [] | |
| parser = argparse.ArgumentParser(description="获取.blend文件场景信息") | |
| parser.add_argument("--blend", type=str, required=True, | |
| help=".blend文件路径") | |
| return parser.parse_args(argv) | |
| def is_indoor_object(obj_name): | |
| """判断对象是否是室内对象""" | |
| name_lower = obj_name.lower() | |
| return any(kw in name_lower for kw in INDOOR_KEYWORDS) | |
| def get_scene_cameras(): | |
| """ | |
| 获取场景中的所有相机 | |
| Returns: | |
| cameras: 相机信息列表 [{"name": str, "position": [x,y,z], "rotation": [x,y,z]}, ...] | |
| """ | |
| cameras = [] | |
| for obj in bpy.context.scene.objects: | |
| if obj.type == 'CAMERA': | |
| loc = obj.location | |
| rot = obj.rotation_euler | |
| cameras.append({ | |
| "name": obj.name, | |
| "position": [loc.x, loc.y, loc.z], | |
| "rotation": [rot.x, rot.y, rot.z] | |
| }) | |
| return cameras | |
| def get_scene_bounds(indoor_only=True): | |
| """ | |
| 获取场景中mesh物体的边界框(Blender坐标系:X右, Y前, Z上) | |
| Args: | |
| indoor_only: 是否仅考虑室内对象(默认True) | |
| Returns: | |
| bounds_min: 边界框最小坐标 [x, y, z] | |
| bounds_max: 边界框最大坐标 [x, y, z] | |
| center: 几何中心 [x, y, z] | |
| indoor_count: 室内对象数量 | |
| """ | |
| min_coords = [float('inf'), float('inf'), float('inf')] | |
| max_coords = [float('-inf'), float('-inf'), float('-inf')] | |
| mesh_count = 0 | |
| indoor_count = 0 | |
| for obj in bpy.context.scene.objects: | |
| if obj.type == 'MESH': | |
| mesh_count += 1 | |
| # 如果启用室内过滤,检查对象名称 | |
| if indoor_only and not is_indoor_object(obj.name): | |
| continue | |
| indoor_count += 1 | |
| # 获取世界坐标下的边界框 | |
| for corner in obj.bound_box: | |
| world_corner = obj.matrix_world @ Vector(corner) | |
| for i in range(3): | |
| min_coords[i] = min(min_coords[i], world_corner[i]) | |
| max_coords[i] = max(max_coords[i], world_corner[i]) | |
| # 如果没有找到任何符合条件的mesh | |
| if min_coords[0] == float('inf'): | |
| if indoor_only: | |
| print(f"[WARN] 未找到室内对象,回退到全场景边界框", file=sys.stderr) | |
| return get_scene_bounds(indoor_only=False) | |
| else: | |
| print(f"[WARN] 未找到任何mesh对象,使用默认边界框", file=sys.stderr) | |
| min_coords = [-5, -5, 0] | |
| max_coords = [5, 5, 3] | |
| indoor_count = 0 | |
| # 计算几何中心 | |
| center = [ | |
| (min_coords[0] + max_coords[0]) / 2, | |
| (min_coords[1] + max_coords[1]) / 2, | |
| (min_coords[2] + max_coords[2]) / 2 | |
| ] | |
| mode_str = "室内对象" if indoor_only else "全场景" | |
| print(f"[INFO] 边界框模式: {mode_str}", file=sys.stderr) | |
| print(f"[INFO] 找到 {indoor_count}/{mesh_count} 个对象", file=sys.stderr) | |
| print(f"[INFO] 边界框: min={[f'{x:.2f}' for x in min_coords]}, max={[f'{x:.2f}' for x in max_coords]}", file=sys.stderr) | |
| print(f"[INFO] 几何中心: [{center[0]:.2f}, {center[1]:.2f}, {center[2]:.2f}]", file=sys.stderr) | |
| return min_coords, max_coords, center, indoor_count | |
| def main(): | |
| args = parse_args() | |
| print(f"[INFO] 打开.blend文件: {args.blend}", file=sys.stderr) | |
| # 打开.blend文件 | |
| bpy.ops.wm.open_mainfile(filepath=args.blend) | |
| # 1. 检测场景中的相机 | |
| cameras = get_scene_cameras() | |
| print(f"[INFO] 检测到 {len(cameras)} 个相机", file=sys.stderr) | |
| camera_info = None | |
| first_frame_position = None | |
| position_source = "none" | |
| if cameras: | |
| # 使用第一个相机的位置作为参考 | |
| cam = cameras[0] | |
| camera_info = cam | |
| first_frame_position = cam["position"] | |
| position_source = "camera" | |
| print(f"[INFO] 使用相机 '{cam['name']}' 的位置作为第一帧参考", file=sys.stderr) | |
| print(f"[INFO] 相机位置: [{cam['position'][0]:.2f}, {cam['position'][1]:.2f}, {cam['position'][2]:.2f}]", file=sys.stderr) | |
| # 2. 获取室内对象边界框 | |
| bounds_min, bounds_max, center, indoor_count = get_scene_bounds(indoor_only=True) | |
| # 如果没有相机,使用室内边界框中心 | |
| if first_frame_position is None: | |
| first_frame_position = center | |
| position_source = "indoor_bounds_center" | |
| print(f"[INFO] 无相机,使用室内边界框中心作为第一帧位置", file=sys.stderr) | |
| # 3. 获取场景单位比例(用于将米转换为场景单位) | |
| unit_scale = bpy.context.scene.unit_settings.scale_length | |
| unit_system = bpy.context.scene.unit_settings.system | |
| print(f"[INFO] 场景单位: scale={unit_scale}, system={unit_system}", file=sys.stderr) | |
| # 输出JSON格式(供batch_render.py解析) | |
| result = { | |
| "bounds_min": bounds_min, | |
| "bounds_max": bounds_max, | |
| "center": center, | |
| "first_frame_position": first_frame_position, | |
| "position_source": position_source, | |
| "camera": camera_info, | |
| "cameras_count": len(cameras), | |
| "indoor_objects_count": indoor_count, | |
| "unit_scale": unit_scale, # 用于米->场景单位转换 | |
| "coordinate_system": "blender_z_up" # X右, Y前, Z上 | |
| } | |
| # 使用特殊前缀,便于batch_render.py解析 | |
| print(f"[BOUNDS] {json.dumps(result)}") | |
| if __name__ == "__main__": | |
| main() | |