275 lines
10 KiB
Python
275 lines
10 KiB
Python
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, '完成')
|