Blender 提取相机位姿教程

Blender 提取相机位姿教程

本教程将指导您如何使用Blender的Python API提取相机位姿,并将其保存到JSON和CSV文件中。我们将逐步分析代码,并提供一个完整的示例。

假设你在blender中已经设计好了相机的运动轨迹等

1. 环境准备

假设你在blender中已经设计好了相机的运动轨迹等

首先,确保您已经安装了Blender,并且可以通过命令行运行Blender。您可以通过以下命令检查Blender是否安装正确:

blender --version
2. 代码

以下是代码的逐步分析:

导入必要的库
import bpy  # 导入Blender的Python API
import numpy as np  # 导入NumPy库,用于矩阵操作
import json  # 导入JSON库,用于处理JSON文件
import random  # 导入随机库
import os  # 导入操作系统接口
import sys  # 导入系统接口
import csv  # 导入CSV库

这些库用于与Blender交互、处理矩阵、读写JSON和CSV文件等。

获取当前场景和相机对象
scene = bpy.context.scene  # 获取当前场景
data = []  # 用于存储所有相机的所有帧的数据
cameras = [obj for obj in scene.objects if obj.type == 'CAMERA']  # 获取场景中的所有相机

这里我们获取当前场景和所有相机对象。

获取网格对象并提取命名格式中的前缀
mesh_objects = [obj for obj in scene.objects if obj.type == 'MESH']
if mesh_objects:
    selected_mesh = random.choice(mesh_objects)
    mesh_prefix = selected_mesh.name.split('_')[0]
else:
    print("错误: 没有找到网格对象")
    sys.exit(1)

我们随机选择一个网格对象,并提取其名称前缀。

遍历每个相机和每一帧,提取相机位姿
for cam in cameras:  # 遍历每个相机
    for frame in range(scene.frame_start, scene.frame_end + 1):  # 遍历每一帧
        scene.frame_set(frame)  # 设置当前帧
        bpy.context.view_layer.update()  # 更新视图层
        bpy.context.evaluated_depsgraph_get().update()  # 更新依赖图

        # 获取相机外参矩阵 (Extrinsic Parameters)
        matrix_world = cam.matrix_world.copy()  # 复制相机的世界矩阵
        matrix_world_np = np.array(matrix_world)  # 转换为NumPy数组
        translation = matrix_world_np[:3, 3]  # 提取相机的位置 (translation vector)
        rotation_matrix = matrix_world_np[:3, :3]  # 提取相机的旋转矩阵 (rotation matrix)

        # 获取相机内参矩阵 (Intrinsic Parameters)
        cam_data = cam.data  # 获取相机数据
        focal_length = cam_data.lens  # 获取焦距 (focal length)
        sensor_width = cam_data.sensor_width  # 获取传感器宽度
        sensor_height = cam_data.sensor_height  # 获取传感器高度
        resolution_x = scene.render.resolution_x  # 获取渲染分辨率的X值
        resolution_y = scene.render.resolution_y  # 获取渲染分辨率的Y值
        scale = scene.render.resolution_percentage / 100.0  # 获取分辨率比例

        pixel_aspect_ratio = scene.render.pixel_aspect_x / scene.render.pixel_aspect_y  # 获取像素长宽比
        s_u = resolution_x * scale / sensor_width  # 计算像素宽度
        s_v = resolution_y * scale * pixel_aspect_ratio / sensor_height  # 计算像素高度

        u_0 = resolution_x * scale / 2.0  # 计算主点的U坐标
        v_0 = resolution_y * scale / 2.0  # 计算主点的V坐标

        # 生成路径信息
        frame_str = f"{frame:04d}"
        camera_suffix = cam.name.split('_')[-1] if '_' in cam.name else cam.name
        path = f"{mesh_prefix}/{frame_str}_{camera_suffix}.png"

        # 将数据添加到列表中
        data.append({
            'name': mesh_prefix,  # 网格对象的前缀
            'pose': {
                'frame': frame,  # 帧数
                'camera_name': cam.name,  # 相机名字
                'camera_position': {
                    'x': translation[0],
                    'y': translation[1],
                    'z': translation[2]
                },  # 相机位置的X、Y、Z坐标
                'camera_rotation_matrix': rotation_matrix.tolist(),  # 旋转矩阵
                'focal_length': focal_length,  # 焦距
                'principal_point': {
                    'u0': u_0,
                    'v0': v_0
                }  # 主点的U、V坐标
            },
            'path': path  # 路径信息
        })
        print(f"已处理相机 {cam.name} 的帧 {frame}")  # 打印提示信息

在这里,我们遍历每个相机和每一帧,提取相机的外参和内参矩阵,并将数据存储到列表中。

定义基础路径并检查和创建JSON文件
base_path = r'D:\your\output\path'  # 修改为自己的输出路径

# 检查并创建JSON文件
json_file_path = os.path.join(base_path, 'GT', 'json', 'GT.json')
if not os.path.exists(os.path.dirname(json_file_path)):
    os.makedirs(os.path.dirname(json_file_path))

if os.path.exists(json_file_path):
    try:
        with open(json_file_path, 'r', encoding='utf-8') as json_file:
            existing_data = json.load(json_file)
    except json.JSONDecodeError:
        existing_data = []
else:
    existing_data = []

# 将新数据追加到现有数据中
existing_data.extend(data)

# 将数据写入JSON文件
with open(json_file_path, 'w', encoding='utf-8') as json_file:
    json.dump(existing_data, json_file, indent=4, ensure_ascii=False)
    print("JSON文件已更新并写入数据")  # 打印提示信息

这里我们定义基础路径,并检查和创建JSON文件,将提取的数据写入JSON文件中。

检查和创建CSV文件
# 检查并创建CSV文件
csv_file_path = os.path.join(base_path, 'GT', 'csv', 'GT.csv')
if not os.path.exists(os.path.dirname(csv_file_path)):
    os.makedirs(os.path.dirname(csv_file_path))

csv_headers = ['name', 'frame', 'camera_name', 'camera_position_x', 'camera_position_y', 'camera_position_z', 'camera_rotation_matrix', 'focal_length', 'principal_point_u0', 'principal_point_v0', 'path']

# 检查CSV文件是否存在
file_exists = os.path.isfile(csv_file_path)

with open(csv_file_path, 'a', newline='', encoding='utf-8') as csv_file:
    writer = csv.DictWriter(csv_file, fieldnames=csv_headers)
    if not file_exists:
        writer.writeheader()  # 如果文件不存在,写入表头
    for item in existing_data:
        writer.writerow({
            'name': item['name'],
            'frame': item['pose']['frame'],
            'camera_name': item['pose']['camera_name'],
            'camera_position_x': item['pose']['camera_position']['x'],
            'camera_position_y': item['pose']['camera_position']['y'],
            'camera_position_z': item['pose']['camera_position']['z'],
            'camera_rotation_matrix': item['pose']['camera_rotation_matrix'],
            'focal_length': item['pose']['focal_length'],
            'principal_point_u0': item['pose']['principal_point']['u0'],
            'principal_point_v0': item['pose']['principal_point']['v0'],
            'path': item['path']
        })
    print("CSV文件已更新并写入数据")  # 打印提示信息

这里我们检查和创建CSV文件,并将提取的数据写入CSV文件中。

3. 完整示例代码

以下是完整的示例代码:

import bpy
import numpy as np
import json
import random
import os
import sys
import csv

scene = bpy.context.scene
data = []
cameras = [obj for obj in scene.objects if obj.type == 'CAMERA']

mesh_objects = [obj for obj in scene.objects if obj.type == 'MESH']
if mesh_objects:
    selected_mesh = random.choice(mesh_objects)
    mesh_prefix = selected_mesh.name.split('_')[0]
else:
    print("错误: 没有找到网格对象")
    sys.exit(1)

for cam in cameras:
    for frame in range(scene.frame_start, scene.frame_end + 1):
        scene.frame_set(frame)
        bpy.context.view_layer.update()
        bpy.context.evaluated_depsgraph_get().update()

        matrix_world = cam.matrix_world.copy()
        matrix_world_np = np.array(matrix_world)
        translation = matrix_world_np[:3, 3]
        rotation_matrix = matrix_world_np[:3, :3]

        cam_data = cam.data
        focal_length = cam_data.lens
        sensor_width = cam_data.sensor_width
        sensor_height = cam_data.sensor_height
        resolution_x = scene.render.resolution_x
        resolution_y = scene.render.resolution_y
        scale = scene.render.resolution_percentage / 100.0

        pixel_aspect_ratio = scene.render.pixel_aspect_x / scene.render.pixel_aspect_y
        s_u = resolution_x * scale / sensor_width
        s_v = resolution_y * scale * pixel_aspect_ratio / sensor_height

        u_0 = resolution_x * scale / 2.0
        v_0 = resolution_y * scale / 2.0

        frame_str = f"{frame:04d}"
        camera_suffix = cam.name.split('_')[-1] if '_' in cam.name else cam.name
        path = f"{mesh_prefix}/{frame_str}_{camera_suffix}.png"

        data.append({
            'name': mesh_prefix,
            'pose': {
                'frame': frame,
                'camera_name': cam.name,
                'camera_position': {
                    'x': translation[0],
                    'y': translation[1],
                    'z': translation[2]
                },
                'camera_rotation_matrix': rotation_matrix.tolist(),
                'focal_length': focal_length,
                'principal_point': {
                    'u0': u_0,
                    'v0': v_0
                }
            },
            'path': path
        })
        print(f"已处理相机 {cam.name} 的帧 {frame}")

base_path = r'D:\your\output\path'

json_file_path = os.path.join(base_path, 'GT', 'json', 'GT.json')
if not os.path.exists(os.path.dirname(json_file_path)):
    os.makedirs(os.path.dirname(json_file_path))

if os.path.exists(json_file_path):
    try:
        with open(json_file_path, 'r', encoding='utf-8') as json_file:
            existing_data = json.load(json_file)
    except json.JSONDecodeError:
        existing_data = []
else:
    existing_data = []

existing_data.extend(data)

with open(json_file_path, 'w', encoding='utf-8') as json_file:
    json.dump(existing_data, json_file, indent=4, ensure_ascii=False)
    print("JSON文件已更新并写入数据")

csv_file_path = os.path.join(base_path, 'GT', 'csv', 'GT.csv')
if not os.path.exists(os.path.dirname(csv_file_path)):
    os.makedirs(os.path.dirname(csv_file_path))

csv_headers = ['name', 'frame', 'camera_name', 'camera_position_x', 'camera_position_y', 'camera_position_z', 'camera_rotation_matrix', 'focal_length', 'principal_point_u0', 'principal_point_v0', 'path']

file_exists = os.path.isfile(csv_file_path)

with open(csv_file_path, 'a', newline='', encoding='utf-8') as csv_file:
    writer = csv.DictWriter(csv_file, fieldnames=csv_headers)
    if not file_exists:
        writer.writeheader()
    for item in existing_data:
        writer.writerow({
            'name': item['name'],
            'frame': item['pose']['frame'],
            'camera_name': item['pose']['camera_name'],
            'camera_position_x': item['pose']['camera_position']['x'],
            'camera_position_y': item['pose']['camera_position']['y'],
            'camera_position_z': item['pose']['camera_position']['z'],
            'camera_rotation_matrix': item['pose']['camera_rotation_matrix'],
            'focal_length': item['pose']['focal_length'],
            'principal_point_u0': item['pose']['principal_point']['u0'],
            'principal_point_v0': item['pose']['principal_point']['v0'],
            'path': item['path']
        })
    print("CSV文件已更新并写入数据")
4. 命令行脚本讲解

以下是如何通过命令行运行Blender脚本的示例:

cd "D:\yxq\software\Blender Foundation\Blender 4.2\"

blender -b D:\。。。\xxx.blend -P D:\。。。\testData.py
  • cd "D:\...\Blender Foundation\Blender 4.2\":切换到Blender的安装目录。
  • blender -b xxx.blend -P D:\...\提取json.py:以后台模式运行Blender,加载指定的.blend文件,并执行指定的Python脚本。
  • blender -b zzz.blend -P D:\...\testData.py:同样以后台模式运行Blender,加载.blend文件,并执行另一个Python脚本。

通过以上步骤,您可以提取Blender中相机的位姿,并将其保存到JSON和CSV文件中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值