Files
make2/apps/fix_3d.py
T
2024-12-27 11:34:28 +08:00

275 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import sys, os, oss2, bpy, bmesh, requests, json, shutil, time, platform
from PIL import Image
if platform.system() == 'Windows':
sys.path.append('e:\\libs\\')
else:
sys.path.append('/data/deploy/make3d/make2/libs/')
import config, libs, libs_db
def bmesh_copy_from_object(obj, transform=True, triangulate=True, apply_modifiers=False):
"""Returns a transformed, triangulated copy of the mesh"""
assert obj.type == 'MESH'
if apply_modifiers and obj.modifiers:
depsgraph = bpy.context.evaluated_depsgraph_get()
obj_eval = obj.evaluated_get(depsgraph)
me = obj_eval.to_mesh()
bm = bmesh.new()
bm.from_mesh(me)
obj_eval.to_mesh_clear()
else:
me = obj.data
if obj.mode == 'EDIT':
bm_orig = bmesh.from_edit_mesh(me)
bm = bm_orig.copy()
else:
bm = bmesh.new()
bm.from_mesh(me)
if transform:
matrix = obj.matrix_world.copy()
if not matrix.is_identity:
bm.transform(matrix)
matrix.translation.zero()
if not matrix.is_identity:
bm.normal_update()
if triangulate:
bmesh.ops.triangulate(bm, faces=bm.faces)
return bm
def down_obj_fromoss(pid, print_type=1, order_id=None):
# print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品
print('开始下载obj文件...' , pid)
if not order_id is None:
path = os.path.join(workdir, f'{pid}_{order_id}')
else:
path = os.path.join(workdir, pid)
if not os.path.exists(path): os.makedirs(path)
# 根据前缀获取文件列表
prefix = f'objs/print/{pid}/'
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix)
find = False
for file in filelist:
filename = file.key.split('/')[-1]
if filename == '': continue
if filename.endswith(f'{pid}.obj'):
find = True
localfile = os.path.join(path, filename)
res = oss_client.get_object_to_file(file.key, localfile)
print(f'下载文件:{file.key},状态:{res.status}')
def down_auto_obj_fromoss(pid, print_type=1, order_id=None):
# print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品
print('开始下载obj文件...' , pid)
if not order_id is None:
path = os.path.join(workdir, f'{pid}_{order_id}')
else:
path = os.path.join(workdir, pid)
if not os.path.exists(path): os.makedirs(path)
# 根据前缀获取文件列表
prefix = f'objs/auto/{pid}/'
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix)
find = False
for file in filelist:
filename = file.key.split('/')[-1]
if filename == '': continue
if filename.endswith(f'{pid}.obj'):
find = True
localfile = os.path.join(path, filename)
res = oss_client.get_object_to_file(file.key, localfile)
print(f'下载文件:{localfile},状态:{res.status}')
def find_obj(pid):
find = False
if not os.path.exists(os.path.join(workdir, pid, f'{pid}.mtl')):
print('没有找到obj模型文件,开始下载')
down_obj_fromoss(pid)
if os.path.exists(os.path.join(workdir, pid, f'{pid}.jpg')):
shutil.move(os.path.join(workdir, pid, f'{pid}.jpg'), os.path.join(workdir, pid, f'{pid}Tex1.jpg'))
with open(os.path.join(workdir, pid, f'{pid}.mtl'), 'r') as f:
lines = f.readlines()
lines = [line.replace(f'map_Kd {pid}.jpg', f'map_Kd {pid}Tex1.jpg') for line in lines]
with open(os.path.join(workdir, pid, f'{pid}.mtl'), 'w') as f:
f.writelines(lines)
filelist = os.listdir(os.path.join(workdir, pid))
for filename in filelist:
if '9cm' in filename:
find = True
return filename
for filename in filelist:
if f'{pid}.obj' in filename:
find = True
return filename
for filename in filelist:
if '.obj' in filename:
find = True
return filename
print('没有找到obj模型文件')
return ''
def resize_photo(filename, ratio, quality_value=80):
print('正在压缩图片:', filename)
img = Image.open(filename)
width, height = img.size
width = int(width * ratio)
height = int(height * ratio)
img = img.resize((width, height))
img.save(filename, optimize=True, quality=quality_value)
def fix_link_texture(pid):
path = os.path.join(workdir,action,pid)
if os.path.exists(os.path.join(path, f'{pid}Tex1_old.jpg')):
shutil.copy(os.path.join(path, f'{pid}Tex1_old.jpg'), os.path.join(path, f'{pid}Tex1_decimate.jpg'))
elif os.path.exists(os.path.join(path, f'{pid}.jpg')):
shutil.copy(os.path.join(path, f'{pid}.jpg'), os.path.join(path, f'{pid}Tex1_decimate.jpg'))
else:
shutil.copy(os.path.join(path, f'{pid}Tex1.jpg'), os.path.join(path, f'{pid}Tex1_decimate.jpg'))
resize_photo(os.path.join(path, f'{pid}Tex1_decimate.jpg'), 0.5)
# 修改pid_original.mtl文件中的贴图为pid_old.jpg
with open(os.path.join(path, f'{pid}_decimate.mtl'), 'r') as f:
lines = f.readlines()
for i in range(len(lines)):
if lines[i].startswith('map_Kd'):
lines[i] = f'map_Kd {pid}Tex1_decimate.jpg\n'
break
with open(os.path.join(path, f'{pid}_decimate.mtl'), 'w') as f:
f.writelines(lines)
f.close()
def main(pid, faces_per_person,action="print"):
start_time = time.time()
print('{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始处理pid: {pid}')
# 读取obj文件
path = os.path.join(workdir,action,pid)
libs.down_obj_from_oss(workdir,pid,action)
prefixJpg = "Tex1.jpg"
if action == "auto":
prefixJpg = ".jpg"
resize_photo(os.path.join(path, f'{pid}{prefixJpg}'), 0.5)
obj_file = f'{pid}.obj'
bpy.ops.wm.read_homefile()
# bpy.context.preferences.view.language = 'en_US'
bpy.ops.object.delete(use_global=False, confirm=False)
bpy.context.scene.unit_settings.scale_length = 0.001
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS'
bpy.context.scene.unit_settings.mass_unit = 'GRAMS'
bpy.ops.import_scene.obj(filepath=os.path.join(path, obj_file))
obj = bpy.context.selected_objects[0]
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
scale = 90 / obj.dimensions.y
obj.scale = (scale, scale, scale)
bm = bmesh_copy_from_object(obj)
obj_volume = round(bm.calc_volume() / 1000, 3)
print('volume:', obj_volume)
print('weight:', obj_volume * 1.26, 'g')
faces = len(obj.data.polygons)
print('faces:', faces)
with open(os.path.join(path, f'{pid}_decimate.txt'), 'w') as f:
lines = [f'faces:{faces}\n', f'volume:{obj_volume}\n']
f.writelines(lines)
f.close()
# upload_res = requests.get(f'{upload_obj_volume_url}?pid={pid}&order_id={order_id}&faces={faces}&volume={obj_volume}')
# print('上传模型体积:', upload_res.text)
# 生成数字模型
headcount = get_headcount(pid)
faces_dest = faces_per_person * headcount
# 减面
faces_current = len(obj.data.polygons)
bpy.ops.object.modifier_add(type='DECIMATE')
bpy.context.object.modifiers["Decimate"].ratio = faces_dest / faces_current
bpy.ops.object.modifier_apply(modifier="Decimate")
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
bpy.context.object.location[0] = 0
bpy.context.object.location[1] = 0
# bpy.context.object.rotation_euler = (0, 0, 0)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bpy.ops.export_scene.obj(filepath=os.path.join(path, f'{pid}_decimate.obj'))
fix_link_texture(pid)
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(path, f'{pid}.blend'))
bpy.ops.export_scene.gltf(filepath=os.path.join(path, f'{pid}_decimate.glb'), export_format='GLB', export_apply=True, export_jpeg_quality=80)
bpy.ops.wm.quit_blender()
input = os.path.join(path, f'{pid}_decimate.obj')
output = os.path.join(path, f'{pid}_decimate.glb')
# os.system(f'gltfpack -c -i {input} -o {output}')
oss_client.put_object_from_file(f'glbs/3d/{pid}.glb', output)
# print('上传glb文件:', f'glbs/3d/{pid}.glb 完成')
for filename in os.listdir(path):
if not 'decimate' in filename:
os.remove(os.path.join(path, filename))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 处理完成,共费时{libs.diff_time(start_time)}')
def get_headcount(pid):
res = requests.get(get_printinfo_url, params={'id': pid})
print('get_printsize_url:', res.url)
print('res:', res.text)
if res.status_code != 200:
print('获取人数失败,程序退出')
exit(1)
res = json.loads(res.text)
return res['data']['headcount']
if __name__ == '__main__':
if platform.system() == 'Windows':
workdir = 'd:\\3d\\'
else:
workdir = '/data/datasets/3d/'
get_printinfo_url = config.urls['get_printinfo_url']
upload_obj_volume_url = 'https://mp.api.suwa3d.com/api/physical/add' # ?pid=1&order_id=1&faces=1&volume=1
ali_oss = {
'access_key_id': 'LTAI5tSReWm8hz7dSYxxth8f',
'access_key_secret': '8ywTDF9upPAtvgXtLKALY2iMYHIxdS',
'facebody_endpoint': 'facebody.cn-shanghai.aliyuncs.com',
'endpoint': 'oss-cn-shanghai.aliyuncs.com',
'bucket_name': 'suwa3d-securedata',
}
oss_client = oss2.Bucket(oss2.Auth(ali_oss['access_key_id'], ali_oss['access_key_secret']), ali_oss['endpoint'], ali_oss['bucket_name'])
faces_per_person = 120000
action = "print"
if len(sys.argv) == 3:
pids = sys.argv[2].split(',')
action = sys.argv[1]
for pid in pids:
main(pid, faces_per_person,action)
else:
pass
# # 根据前缀获取文件列表
# prefix = f'glbs/3d/'
# filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix)
# start = False
# for file in filelist:
# filename = file.key.split('/')[-1]
# if filename.endswith('.glb'):
# pid = filename.split('.')[0]
# if pid == '99531':
# start = True
# continue
# elif not start:
# print('跳过pid:', pid)
# continue
# if start:
# print('正在处理pid:', pid)
# main(pid, faces_per_person)
# print('处理pid:', pid, '完成')