以相机视作激光源,按照渲染图片的范围对指定物体进行扫描
Blender ray_cast文档
需要注意的是,各参数均为目标的局部坐标系下,故调用的时候需要将全局坐标系下的坐标转换到目标的局部坐标系下,对输出的结果也应转换到全局坐标系下
import bpy
from mathutils import *
import numpy as np
import pickle
# 获取相机对象
camera = bpy.data.objects['Camera']
#
goal = bpy.data.objects['猴头']
focal_length = 50 # mm
sensor_width = 36 # mm
# 渲染图片大小
img_width = 640 # px
img_height = 480 # px
result = np.zeros((640,480,3))
# 计算像元大小
meta_pix_size = sensor_width / img_width # mm/px
z = -1
# 假设指示器距离相机1m
def laser_scan(origin, direction):
global goal, camera
# 选中目标
nearest_point = None
result = goal.ray_cast(origin, direction, distance=200)
if result[0]:
nearest_point = result[1]
# 将结果转换到全局坐标系下
nearest_point = goal.matrix_world @ nearest_point
else:
nearest_point = (0,0,0)
return nearest_point
# 将相机坐标转换到目标的局部坐标系下
origin = camera.location - goal.location
# 以下循环利用几何关系,假定存在深度为1的物体A,各像素对应下的物体A的空间坐标,依次获取方向向量——从相机中心发出一束激光,从左往右,从上往下遍历扫描
for h in range(img_height):
delta_h_pix = h-img_height/2
delta_h_distance = -1*delta_h_pix * meta_pix_size # mm
y = delta_h_distance / focal_length # m
for w in range(img_width):
#print(f"{h}:{w}")
delta_w_pix = w-img_width/2
delta_w_distance = delta_w_pix * meta_pix_size # mm
x = delta_w_distance / focal_length # m
# 将按照像素位置和深度计算的相机局部坐标系下的目标点转换到全局坐标系下
global_pos = camera.matrix_world @ Vector((x,y,z))
# 计算方向
direction = global_pos - camera.location
point = laser_scan(origin, direction)
result[w,h,:]=point
fout = open(r'pointclouds.pkl', 'wb')
pickle.dump(result, fout)
fout.close()
场景渲染图
场景图
输出结果可视化