Browse Source

渲染图处理

master
dongchangxi 7 months ago
parent
commit
5c1d4fc1b9
  1. 31
      tools/pic_for_obj/image_rander_small.py
  2. 302
      tools/pic_for_obj/image_rander_small原始.py

31
tools/pic_for_obj/image_rander_small.py

@ -86,9 +86,8 @@ def rander_image_and_check(args): @@ -86,9 +86,8 @@ def rander_image_and_check(args):
"""渲染图片"""
# 列出obj列表
pid = args.pid
pid_file_dir = os.path.join(args.input)
pid_file_dir = os.path.join(args.input,pid)
if not os.path.exists(pid_file_dir):
print(f"{pid_file_dir} 文件夹不存在")
return
obj_file_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".obj")]
if len(obj_file_list)==0:
@ -104,7 +103,6 @@ def rander_image_and_check(args): @@ -104,7 +103,6 @@ def rander_image_and_check(args):
is_rander = False
# 根据obj表渲染图片
for obj_file in sorted_obj_file_list:
print(f"开始执行blender渲染--{obj_file}")
start = time.time()
# 初始化blender环境
bpy.ops.wm.read_homefile()
@ -124,12 +122,12 @@ def rander_image_and_check(args): @@ -124,12 +122,12 @@ def rander_image_and_check(args):
# 导入模型文件
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid}')
start_import = time.time()
print("===============", os.path.join(args.input, obj_file))
print("===============", os.path.join(args.input, pid, obj_file))
with stdout_redirected(to=os.devnull):
bpy.ops.import_scene.obj(filepath=os.path.join(args.input,obj_file))
bpy.ops.import_scene.obj(filepath=os.path.join(args.input, pid, obj_file))
print(
f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid} done in {time.time() - start_import:.2f}s')
texture_path = os.path.join(args.input,texture_file)
texture_path = os.path.join(args.input, pid, texture_file)
materials = bpy.data.materials
for material in materials:
# 确保材质使用节点树
@ -145,7 +143,17 @@ def rander_image_and_check(args): @@ -145,7 +143,17 @@ def rander_image_and_check(args):
imported_object = bpy.context.selected_objects[0] # 假设只导入一个模型
# 获取模型的边界框(bounding box)
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
min_z = min(corner.z for corner in bbox_corners)
bpy.ops.object.select_all(action='DESELECT')
imported_object.select_set(True)
bpy.context.view_layer.objects.active = imported_object
# Move object down so lowest point is at origin
imported_object.location.z -= min_z
# Apply transformations
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
# 获取最高点的 Y 值
model_height = max(corner.z for corner in bbox_corners)
@ -223,8 +231,10 @@ def rander_image_and_check(args): @@ -223,8 +231,10 @@ def rander_image_and_check(args):
os.makedirs(os.path.join(args.output, args.len_type,
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}', camera_id),
exist_ok=True)
png_name = f"{pid}_pic.png"
png_out_path = os.path.join(args.output,png_name)
png_name = f"{pid}_{column + 1}.png"
png_out_path = os.path.join(args.output, args.len_type,
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}',
camera_id, png_name)
bpy.context.scene.render.filepath = png_out_path
bpy.context.scene.render.image_settings.file_format = 'PNG'
@ -246,7 +256,7 @@ def rander_image_and_check(args): @@ -246,7 +256,7 @@ def rander_image_and_check(args):
def get_args():
argparser = argparse.ArgumentParser(description='Render the camera in the studio')
argparser.add_argument('-pid', '--pid', type=str, default='', help='pid of the model')
argparser.add_argument('-pid', '--pid', type=str, default='236479', help='pid of the model')
argparser.add_argument('-i', '--input', type=str, default='/data/datasets_20t/obj_rander/',
help='input models path')
argparser.add_argument('-o', '--output', type=str, default='/data/datasets_20t/render_images/',
@ -258,7 +268,7 @@ def get_args(): @@ -258,7 +268,7 @@ def get_args():
argparser.add_argument('-r_x', '--resolution_x', type=int, default='768', help='render resolution_x')
argparser.add_argument('-r_y', '--resolution_y', type=int, default='1024', help='render resolution_y')
argparser.add_argument('-s', '--sample', type=int, default='512', help='render sample')
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='D://make2/tools/pic_for_obj/hdrs/studio_small_08_2k.exr',
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='/data/datasets/hdrs/studio_small_08_2k.exr',
help='enviroment texture path')
args = argparser.parse_args()
@ -285,7 +295,6 @@ def rander_image_run(args): @@ -285,7 +295,6 @@ def rander_image_run(args):
if not os.path.exists(args.output):
os.makedirs(args.output)
try:
print("进入渲染")
rander_image_and_check(args)
except :
print("图片渲染错误",args.pid)

302
tools/pic_for_obj/image_rander_small原始.py

@ -0,0 +1,302 @@ @@ -0,0 +1,302 @@
import math, os, time, sys, bpy, argparse
from tqdm import tqdm
from contextlib import contextmanager
from mathutils import Vector
from PIL import Image
import numpy as np
@contextmanager
def stdout_redirected(to=os.devnull):
'''
import os
with stdout_redirected(to=filename):
print("from Python")
os.system("echo non-Python applications are also supported")
'''
fd = sys.stdout.fileno()
##### assert that Python and C stdio write using the same file descriptor
####assert libc.fileno(ctypes.c_void_p.in_dll(libc, "stdout")) == fd == 1
def _redirect_stdout(to):
sys.stdout.close() # + implicit flush()
os.dup2(to.fileno(), fd) # fd writes to 'to' file
sys.stdout = os.fdopen(fd, 'w') # Python writes to fd
with os.fdopen(os.dup(fd), 'w') as old_stdout:
with open(to, 'w') as file:
_redirect_stdout(to=file)
try:
yield # allow code to be run with the redirected stdout
finally:
_redirect_stdout(to=old_stdout) # restore stdout.
# buffering and flags such as
# CLOEXEC may be different
def check_image_rander(png_image_dir):
"""检查图片生成是否正常"""
image_list = os.listdir(png_image_dir)
if len(image_list)>0:
image_path = os.path.join(png_image_dir,image_list[0])
img = Image.open(image_path)
# 将图像转换为 RGB 模式
img = img.convert("RGB")
# 将图像数据转换为 numpy 数组
img_data = np.array(img)
# 设定颜色分段数
color_bins = 24
# 将颜色分成 24 种,计算颜色区间
# 对每个 RGB 值进行分段
def get_color_bin(value, bins):
return (value * bins) // 256
# 用于存储所有颜色的集合
color_set = set()
# 遍历图像每一个像素
for row in img_data:
for pixel in row:
# 将 RGB 每个通道的值分段
r, g, b = pixel
r_bin = get_color_bin(r, color_bins)
g_bin = get_color_bin(g, color_bins)
b_bin = get_color_bin(b, color_bins)
# 将 RGB 分段后的颜色组合成一个元组
color_set.add((r_bin, g_bin, b_bin))
# 输出不同的颜色数量
print(f"该图像中有 {len(color_set)} 种颜色。")
if len(color_set)>20:
return True
else:
return False
else:
return False
def rander_image_and_check(args):
"""渲染图片"""
# 列出obj列表
pid = args.pid
pid_file_dir = os.path.join(args.input)
if not os.path.exists(pid_file_dir):
print(f"{pid_file_dir} 文件夹不存在")
return
obj_file_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".obj")]
if len(obj_file_list)==0:
print("没有obj文件",pid)
return
texture_path_list = [aa for aa in os.listdir(pid_file_dir) if aa.endswith(".jpg")]
if len(texture_path_list)==0:
print("没有贴图文件",pid)
return
texture_file = texture_path_list[0]
sorted_obj_file_list = sorted(obj_file_list, key=len)
print(sorted_obj_file_list)
is_rander = False
# 根据obj表渲染图片
for obj_file in sorted_obj_file_list:
print(f"开始执行blender渲染--{obj_file}")
start = time.time()
# 初始化blender环境
bpy.ops.wm.read_homefile()
bpy.ops.object.delete(use_global=False, confirm=False)
bpy.context.scene.unit_settings.scale_length = 1
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS'
bpy.context.scene.unit_settings.mass_unit = 'GRAMS'
# bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.render.engine = 'BLENDER_EEVEE' # ('BLENDER_EEVEE', 'BLENDER_WORKBENCH', 'CYCLES')
bpy.context.scene.cycles.feature_set = 'SUPPORTED'
bpy.context.scene.cycles.device = 'GPU'
bpy.context.scene.cycles.preview_samples = args.sample
bpy.context.scene.cycles.samples = args.sample
bpy.context.scene.render.film_transparent = True
bpy.context.scene.cycles.use_progressive_refining = True # 启用渐进式渲染
# 导入模型文件
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid}')
start_import = time.time()
print("===============", os.path.join(args.input, obj_file))
with stdout_redirected(to=os.devnull):
bpy.ops.import_scene.obj(filepath=os.path.join(args.input,obj_file))
print(
f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: import pid: {pid} done in {time.time() - start_import:.2f}s')
texture_path = os.path.join(args.input,texture_file)
materials = bpy.data.materials
for material in materials:
# 确保材质使用节点树
if not material.use_nodes:
material.use_nodes = True
node_tree = material.node_tree
texture_node = node_tree.nodes.new(type='ShaderNodeTexImage')
texture_node.image = bpy.data.images.load(texture_path)
bsdf_node = node_tree.nodes.get('Principled BSDF')
if bsdf_node:
node_tree.links.new(bsdf_node.inputs['Base Color'], texture_node.outputs['Color'])
# 获取模型对象
imported_object = bpy.context.selected_objects[0] # 假设只导入一个模型
# 获取模型的边界框(bounding box)
bbox_corners = [imported_object.matrix_world @ Vector(corner) for corner in imported_object.bound_box]
# 获取最高点的 Y 值
model_height = max(corner.z for corner in bbox_corners)
print(f"The highest Y value of the model is: {model_height}")
# 获取相机的位置 (假设相机位置参数已经在args中设定)
camera_position = args.cameras['1']['position']
# 计算缩放比例:假设模型的高度是1米(或者你可以通过其他方法计算模型的实际高度)
# model_height =25 # 这是一个假设值,实际应用中需要根据模型大小来设定
scale_factor = camera_position / model_height # 根据相机高度和模型高度计算缩放比例
bpy.context.view_layer.objects.active = imported_object
imported_object.select_set(True)
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'})
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# 设置模型的缩放
imported_object.scale = (scale_factor, scale_factor, scale_factor)
bpy.context.object.rotation_euler = (0, 0, 0)
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
bpy.context.object.location[0] = 0 # 移动到特定位置(1m, 1m)是为了让human3d可以正确识别
bpy.context.object.location[1] = 0
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# 设置环境光
bpy.data.scenes['Scene'].world.use_nodes = True
enode = bpy.data.scenes['Scene'].world.node_tree.nodes.new("ShaderNodeTexEnvironment")
enode.image = bpy.data.images.load(args.Env_Texture_path)
bpy.data.scenes['Scene'].world.node_tree.links.new(enode.outputs['Color'],
bpy.data.scenes['Scene'].world.node_tree.nodes[
'Background'].inputs['Color'])
# 初始化相机
camera_lens = 9 # 相机焦距,调整焦距改变镜头广角角度覆盖视野范围
camera_sensor_width = 25.4 # 相机传感器宽度 1英寸 = 25.4mm
camera_angle = 2 * math.pi / args.ps_column # 每台相机的圆心角(弧度)
# 初始相机参数
bpy.ops.object.select_all(action='DESELECT')
obj_camera = bpy.data.objects['Camera']
bpy.context.view_layer.objects.active = obj_camera
obj_camera.select_set(True)
bpy.context.object.data.type = args.len_type
bpy.context.object.data.sensor_width = camera_sensor_width
bpy.context.scene.render.resolution_x = args.resolution_x
bpy.context.scene.render.resolution_y = args.resolution_y
obj_camera.name = 'camera_init'
# 计算并输出所有相机的坐标和旋转
for column in tqdm(range(args.ps_column)):
theta = (column + 1) * camera_angle
x = args.ps_radius * math.sin(theta)
y = args.ps_radius * math.cos(theta)
for camera_id, camera_info in args.cameras.items():
x_ble = x
y_ble = y
z_ble = camera_info['position']
camera_copy_name = obj_camera.copy()
camera_copy_name.data = obj_camera.data.copy()
bpy.context.collection.objects.link(camera_copy_name)
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = camera_copy_name
camera_copy_name.select_set(True)
camera_copy_name.name = f"camera_{column + 1}_{camera_id}"
if args.len_type == 'PERSP':
bpy.context.object.data.lens = camera_info['PERSP_lens']
elif args.len_type == 'ORTHO':
bpy.context.object.data.ortho_scale = camera_info['ORTHO_scale']
camera_copy_name.location = (x_ble, -y_ble, z_ble)
camera_copy_name.rotation_euler = (camera_info['rotation'], 0, theta)
# 渲染图片
bpy.context.scene.camera = bpy.data.objects[camera_copy_name.name] # 切换当前相机
os.makedirs(os.path.join(args.output, args.len_type,
f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}', camera_id),
exist_ok=True)
png_name = f"{pid}_pic.png"
png_out_path = os.path.join(args.output,png_name)
bpy.context.scene.render.filepath = png_out_path
bpy.context.scene.render.image_settings.file_format = 'PNG'
with stdout_redirected():
bpy.ops.render.render(write_still=True)
# delete camera_init
bpy.data.objects.remove(bpy.context.scene.objects['camera_init'])
bpy.data.objects.remove(bpy.context.scene.objects['Light'])
bpy.ops.wm.quit_blender()
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: render pid: {pid} done, time: {time.time() - start:.2f}s')
png_image_dir = os.path.join(args.output, args.len_type,f'{pid}_{args.resolution_x}x{args.resolution_y}_{args.ps_column}',"1",)
time.sleep(2)
if os.path.exists(png_image_dir):
if check_image_rander(png_image_dir):
break
def get_args():
argparser = argparse.ArgumentParser(description='Render the camera in the studio')
argparser.add_argument('-pid', '--pid', type=str, default='', help='pid of the model')
argparser.add_argument('-i', '--input', type=str, default='/data/datasets_20t/obj_rander/',
help='input models path')
argparser.add_argument('-o', '--output', type=str, default='/data/datasets_20t/render_images/',
help='output images path')
argparser.add_argument('-l', '--limit', type=int, default=0, help='render limit')
argparser.add_argument('-ps_radius', '--ps_radius', type=float, default='18', help='photo studio radius, unit: m')
argparser.add_argument('-ps_column', '--ps_column', type=int, default='1', help='photo studio column number')
argparser.add_argument('-t', '--len_type', type=str, default='ORTHO', help='camera lens type, PERSP or.ORTHO')
argparser.add_argument('-r_x', '--resolution_x', type=int, default='768', help='render resolution_x')
argparser.add_argument('-r_y', '--resolution_y', type=int, default='1024', help='render resolution_y')
argparser.add_argument('-s', '--sample', type=int, default='512', help='render sample')
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='D://make2/tools/pic_for_obj/hdrs/studio_small_08_2k.exr',
help='enviroment texture path')
args = argparser.parse_args()
# 相机参数,人体按2米身高,多人拍照覆盖设置
args.cameras = {
'1': {
'position': 2.5, # 1号相机的位置 0.9米高
'rotation': math.radians(86),
'PERSP_lens': 8,
'ORTHO_scale': 3.0,
},
# '2': {
# 'position': 1.8 , # 2号相机的位置 1.8米高
# 'rotation': math.radians(58),
# 'PERSP_lens': 13,
# 'ORTHO_scale': 2.5,
# }
}
return args
def rander_image_run(args):
"""主流程"""
if not os.path.exists(args.output):
os.makedirs(args.output)
try:
print("进入渲染")
rander_image_and_check(args)
except :
print("图片渲染错误",args.pid)
if __name__ == '__main__':
args = get_args()
rander_image_run(args)
#python image_rander_small.py -pid 234161 -i /data/datasets_20t/obj_rander/ -o /data/datasets_20t/render_images/
# python image_rander_small.py -pid 5924 -i /data/datasets_20t/obj_rander/ -o /data/datasets_20t/render_images/
"""
argparser.add_argument('-e', '--Env_Texture_path', type=str, default='/data/datasets/hdrs/studio_small_08_2k.exr',
替换成自己的hdrs路径 bpy 3.4.0
"""
Loading…
Cancel
Save