File size: 6,850 Bytes
5c1bb37 | 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 | #!/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()
|