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, '完成')