Blenderで3Dモデルを読みこみ一括でDepthを撮影する
自分用メモです。
ランダムに色んな角度から連番で保存
import bpy
from mathutils import Vector
import glob
import os
import random
import math
import shutil
# Set the desired number of images to generate
num_images = 10 # For example, generate 10 images
# Path settings
modelpath = 'E:/desktop/obj/'
base_path = 'E:/desktop/base/'
depth_path = 'E:/desktop/depth/'
# Ensure the output folders exist
if not os.path.exists(base_path):
os.makedirs(base_path)
if not os.path.exists(depth_path):
os.makedirs(depth_path)
# Blender settings
bpy.context.scene.use_audio = False
bpy.context.scene.render.use_lock_interface = False
bpy.context.scene.render.use_simplify = True
bpy.context.scene.render.resolution_x = 1024
bpy.context.scene.render.resolution_y = 1024
bpy.context.scene.render.resolution_percentage = 100
# Setup camera function
def setup_camera(scene, target_location, distance=5):
# Find existing cameras or create a new one
cameras = [obj for obj in scene.objects if obj.type == 'CAMERA']
if cameras:
camera = cameras[0]
else:
bpy.ops.object.camera_add(location=(0, -distance, distance/3))
camera = bpy.context.object
camera.name = 'SceneCamera'
camera.data.name = 'SceneCameraData'
# Set camera location and rotation
camera.location = target_location + (camera.location - target_location).normalized() * distance
camera.rotation_mode = 'XYZ'
camera.rotation_euler[0] = math.radians(90) # Point camera downwards
camera.rotation_euler[1] = 0
camera.rotation_euler[2] = 0
scene.camera = camera
# Setup light function
def setup_light(scene, location=(0, -4, 1.5)):
# Find existing lights or create a new one
lights = [obj for obj in scene.objects if obj.type == 'LIGHT']
if not lights:
bpy.ops.object.light_add(type='AREA', location=location)
light = bpy.context.object
light.data.energy = 100
light.data.specular_factor = 0.5
light.data.color = (1.0, 1.0, 1.0)
light.data.size = 1
light.rotation_euler[0] = math.radians(90) # Point light downwards
# Setup depth rendering
def setup_depth_rendering(depth_file_out, name):
scene = bpy.context.scene
scene.use_nodes = True
tree = scene.node_tree
for node in tree.nodes:
tree.nodes.remove(node)
# Setup node graph for depth rendering
render_layers_node = tree.nodes.new('CompositorNodeRLayers')
bpy.context.scene.view_layers["View Layer"].use_pass_z = True
file_output_node = tree.nodes.new('CompositorNodeOutputFile')
file_output_node.base_path = ""
file_output_node.file_slots[0].path = depth_file_out # Use full path directly
normalize_node = tree.nodes.new('CompositorNodeNormalize')
# Add invert color node
invert_node = tree.nodes.new('CompositorNodeInvert')
# Connect nodes
tree.links.new(render_layers_node.outputs['Depth'], normalize_node.inputs[0])
tree.links.new(normalize_node.outputs[0], invert_node.inputs[1]) # Connect to invert node
tree.links.new(invert_node.outputs[0], file_output_node.inputs[0]) # Connect inverted output to file output node
def post_process_images(directory, pattern="*png0001.png"):
# Match all files in the directory with the given pattern
files = glob.glob(os.path.join(directory, pattern))
for file_path in files:
new_file_path = os.path.join(depth_path, os.path.basename(file_path)[:-8])
if os.path.exists(new_file_path):
os.remove(new_file_path)
# Move the file to the depth directory instead of renaming
shutil.move(file_path, new_file_path)
# Main script
scene = bpy.context.scene
target_location = Vector((0, 0, 0))
setup_camera(scene, target_location)
setup_light(scene)
# Gather model files and limit to the desired number of images
files = glob.glob(modelpath + '*.stl') + glob.glob(modelpath + '*.fbx') + glob.glob(modelpath + '*.obj')
random.shuffle(files)
files = files[:num_images]
for i, file in enumerate(files):
try:
print(f'Processing file {i+1}/{num_images}: {file}')
name = f"{i+1:05d}"
base_file_out = os.path.join(depth_path, name + '.png')
depth_file_out = os.path.join(base_path, name + '.png') # Corrected file naming convention
# Clean the scene before importing new model
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.delete()
# Import the model based on its file type
if file.endswith('.fbx'):
bpy.ops.import_scene.fbx(filepath=file)
elif file.endswith('.stl'):
bpy.ops.import_mesh.stl(filepath=file)
elif file.endswith('.obj'):
bpy.ops.import_scene.obj(filepath=file)
# Adjust the model's scale and orientation
obj = bpy.context.selected_objects[0]
dimensions = obj.dimensions
scale_factor = 3.0 / max(dimensions)
obj.scale = (scale_factor, scale_factor, scale_factor)
obj.rotation_mode = 'XYZ'
obj.rotation_euler[2] = random.uniform(0, 2 * math.pi)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# Render depth image and process images
setup_depth_rendering(base_file_out, name)
bpy.context.scene.render.filepath = depth_file_out
bpy.ops.render.render(write_still=True)
post_process_images(depth_path, pattern="*png0001.png")
# Delete the object to clean up for the next iteration
bpy.ops.object.select_all(action='DESELECT')
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete()
except Exception as e:
print(f'Error processing file {i+1}/{num_images}: {file} - {e}')
continue
Blender 2.93.18だと
tree.links.new(render_layers_node.outputs['Depth'], normalize_node.inputs[0])
じゃないと動かなかった。
参照:https://github.com/panmari/stanford-shapenet-renderer/issues/8
①Blender起動
デフォルトオブジェクトを手動で消す
↓
②コンソール表示
③scripttingタブを開き、+Newをクリック
上記のコードをコピペした後、実行すると動きました。
この記事が気に入ったらサポートをしてみませんか?