Colei o código ai, é uma coleção de funções que peguei prontas na internet com algumas poucas que criei.
Ele carrega o objeto, rotaciona e centraliza o objeto. Então cria uma câmera que é rotacionada e elevada no entorno do objeto para múltiplas renderizações. Além disso, tem uma função que calcula as bounding boxes do objeto, já que o objetivo dessa renderização é treinar modelos de detecção de objetos
import bpy
import sys
import os
import math
from mathutils import Vector
import mathutils
from os.path import join as pjoin
import random
def get_center(o):
local_bbox_center = 0.125 * sum((Vector(b) for b in o.bound_box), Vector())
global_bbox_center = o.matrix_world @ local_bbox_center
return global_bbox_center
def clear():
# Select objects by type
for o in bpy.context.scene.objects:
o.select_set(True)
# Call the operator
bpy.ops.object.delete()
def update_camera(camera, focus_point=mathutils.Vector((0.0, 0.0, 0.0)), distance=10.0):
"""
Focus the camera to a focus point and place the camera at a specific distance from that
focus point. The camera stays in a direct line with the focus point.
:param camera: the camera object
:type camera: bpy.types.object
:param focus_point: the point to focus on (default=``mathutils.Vector((0.0, 0.0, 0.0))``)
:type focus_point: mathutils.Vector
:param distance: the distance to keep to the focus point (default=``10.0``)
:type distance: float
"""
looking_direction = camera.location - focus_point
rot_quat = looking_direction.to_track_quat('Z', 'Y')
camera.rotation_euler = rot_quat.to_euler()
new_position = camera.location + rot_quat @ mathutils.Vector((0.0, 0.0, distance))
camera.location = new_position
return new_position
def create_lamp(point, energy=1000):
lamp_data = bpy.data.lights.new(name="Lamp", type='POINT')
lamp_data.energy = energy
lamp_object = bpy.data.objects.new(name="Lamp", object_data=lamp_data)
bpy.context.collection.objects.link(lamp_object)
bpy.context.view_layer.objects.active = lamp_object
lamp_object.location = point
return lamp_object
def load_stl(filepath):
bpy.ops.import_mesh.stl(filepath=filepath)
obj = bpy.context.object
return obj
def center_rotate_obj(obj, angles):
bpy.context.scene.cursor.location = get_center(obj)
bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
obj.location=(0,0,0)
obj.rotation_euler = angles
class Box:
dim_x = 1
dim_y = 1
def __init__(self, min_x, min_y, max_x, max_y, dim_x=dim_x, dim_y=dim_y):
self.min_x = min_x
self.min_y = min_y
self.max_x = max_x
self.max_y = max_y
self.dim_x = dim_x
self.dim_y = dim_y
@property
def x(self):
return round(self.min_x * self.dim_x)
@property
def y(self):
return round(self.dim_y - self.max_y * self.dim_y)
@property
def width(self):
return round((self.max_x - self.min_x) * self.dim_x)
@property
def height(self):
return round((self.max_y - self.min_y) * self.dim_y)
def __str__(self):
return "<Box, x=%i, y=%i, width=%i, height=%i>" % \
(self.x, self.y, self.width, self.height)
def to_tuple(self):
if self.width == 0 or self.height == 0:
return (0, 0, 0, 0)
return (self.x, self.y, self.width, self.height)
def camera_view_bounds_2d(scene, cam_ob, me_ob):
"""
Returns camera space bounding box of mesh object.
Negative 'z' value means the point is behind the camera.
Takes shift-x/y, lens angle and sensor size into account
as well as perspective/ortho projections.
:arg scene: Scene to use for frame size.
:type scene: :class:`bpy.types.Scene`
:arg obj: Camera object.
:type obj: :class:`bpy.types.Object`
:arg me: Untransformed Mesh.
:type me: :class:`bpy.types.Mesh´
:return: a Box object (call its to_tuple() method to get x, y, width and height)
:rtype: :class:`Box`
"""
mat = cam_ob.matrix_world.normalized().inverted()
#me = me_ob.to_mesh(scene, True, 'PREVIEW')
me = me_ob.to_mesh()
me.transform(me_ob.matrix_world)
me.transform(mat)
camera = cam_ob.data
frame = [-v for v in camera.view_frame(scene=scene)[:3]]
camera_persp = camera.type != 'ORTHO'
lx = []
ly = []
for v in me.vertices:
co_local = v.co
z = -co_local.z
if camera_persp:
if z == 0.0:
lx.append(0.5)
ly.append(0.5)
# Does it make any sense to drop these?
#if z <= 0.0:
# continue
else:
frame = [(v / (v.z / z)) for v in frame]
min_x, max_x = frame[1].x, frame[2].x
min_y, max_y = frame[0].y, frame[1].y
x = (co_local.x - min_x) / (max_x - min_x)
y = (co_local.y - min_y) / (max_y - min_y)
lx.append(x)
ly.append(y)
min_x = clamp(min(lx), 0.0, 1.0)
max_x = clamp(max(lx), 0.0, 1.0)
min_y = clamp(min(ly), 0.0, 1.0)
max_y = clamp(max(ly), 0.0, 1.0)
#bpy.data.meshes.remove(me)
r = scene.render
fac = r.resolution_percentage * 0.01
dim_x = r.resolution_x * fac
dim_y = r.resolution_y * fac
return Box(min_x, min_y, max_x, max_y, dim_x, dim_y)
def clamp(x, minimum, maximum):
return max(minimum, min(x, maximum))
def custom2yolo(data):
"""Convert yolo format to voc, both in relative coordinates."""
yolo = []
bbox_width = float(data[3])
bbox_height = float(data[4])
x0 = float(data[1])
y0 = float(data[2])
yolo.append(data[0])
yolo.append(x0 + (bbox_width / 2))
yolo.append(y0 + (bbox_height / 2))
yolo.append(bbox_width)
yolo.append(bbox_height)
return yolo
def convertbb(bb):
return bb[0] / 800.0, bb[1]/600.0, bb[2]/800.0, bb[3]/600.0
def render_scene_bb(scene, cam_ob, obj, obj_number, prefix):
scene.render.resolution_x = 800
scene.render.resolution_y = 600
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.render.image_settings.file_format='PNG'
bpy.context.scene.render.film_transparent = True
bpy.context.scene.render.image_settings.color_mode = 'RGBA'
bpy.data.scenes['Scene'].render.filepath = prefix + '.png'
bpy.ops.render.render(write_still=True)
filepath = prefix + '.txt'
coords = camera_view_bounds_2d(scene, cam_ob, obj).to_tuple()
coords = list(convertbb(coords))
coords.insert(0, obj_number)
coords = custom2yolo(coords)
print(coords)
with open(filepath, "w") as file:
#file.write("%f %f %f %f\n" % convertbb(camera_view_bounds_2d(scene, cam_ob, obj).to_tuple()))
file.write("%i %f %f %f %f\n" % tuple(coords))
##########################################################################################################
is_test = True
root_dir = '/path/to/root'
parts_folder = pjoin(root_dir, 'stl_models')
output_folder = pjoin(root_dir, 'rendered_images')
random.seed(234)
parts = [
{
'name': 'tray',
'path': pjoin(parts_folder, 'tray.STL'),
'rot': (0, 0, 0),
'dists': [280, 560, 1000]
},
{
'name': 'dosing_nozzle',
'path': pjoin(parts_folder, 'dosing_nozzle.STL'),
'rot': (math.pi/2, 0, 0),
'dists': [200, 400, 600]
},
{
'name': 'button_pad',
'path': pjoin(parts_folder, 'button_pad.STL'),
'rot': (0, 0, 0),
'dists': [600, 900, 1200]
},
{
'name': 'part1',
'path': pjoin(parts_folder, 'part1.STL'),
'rot': (-math.pi/2, 0, 0),
'dists': [140, 280, 460]
},
{
'name': 'part3',
'path': pjoin(parts_folder, 'part3.STL'),
'rot': (math.pi/2, 0, 0),
'dists': [140, 280, 460]
},
{
'name': 'part2',
'path': pjoin(parts_folder, 'part2.STL'),
'rot': (math.pi/2, 0, 0),
'dists': [140, 280, 460]
}
]
for obj_number, part in enumerate(parts):
clear()
scene = bpy.context.scene
#load and position mesh
obj = load_stl(part['path'])
center_rotate_obj(obj, part['rot'])
energy = 10**6
cam = bpy.data.cameras.new("Camera")
cam_ob = bpy.data.objects.new("Camera", cam)
bpy.context.collection.objects.link(cam_ob)
scene.camera = cam_ob
scene.camera.data.clip_end = 10000
#write_bounds_2d(filepath, scene, cam_ob, obj, frame_start, frame_end)
distances = part['dists']
if is_test:
vert_angles = (0, math.pi/6, math.pi/3)[:1]
rot_angles = [2*math.pi/8 * x for x in range(8)][:1]
else:
vert_angles = (0, math.pi/6, math.pi/3)
rot_angles = [2*math.pi/8 * x for x in range(8)]
prefix = pjoin(output_folder, part['name'])
i = 0
n = 0
for dist in distances:
n += 1
for vert_angle in vert_angles:
for rot_angle in rot_angles:
z = dist * math.sin(vert_angle)
d = dist * math.cos(vert_angle)
x = d * math.cos(rot_angle)
y = d * math.sin(rot_angle)
l_z = 10 * math.sin(vert_angle)
l_d = 10 * math.cos(vert_angle)
l_x = l_d * math.cos(rot_angle)
l_y = l_d * math.sin(rot_angle)
cam_ob.location = (x, y, z)
pos = update_camera(cam_ob, Vector((0, 0, 0)))
lamp = create_lamp(Vector((0, 0, 0)), energy)
lamp.location = pos + Vector((l_x, l_y, l_z))
fprefix = pjoin(prefix, str(i))
render_scene_bb(scene, cam_ob, obj, obj_number, fprefix)
i += 1
``