commit
fb98172573
287 changed files with 43090 additions and 0 deletions
@ -0,0 +1,324 @@ |
|||||||
|
from ctypes import util |
||||||
|
import os, oss2, time, redis, requests, shutil, sys, subprocess, json, platform |
||||||
|
from PIL import Image, ImageDraw, ImageFont |
||||||
|
from retrying import retry |
||||||
|
import atexit,platform |
||||||
|
import get_preview_image |
||||||
|
# if platform.system() == 'Windows': |
||||||
|
# sys.path.append('libs\\') |
||||||
|
sys.path.append('/home/acprint/code/libs/') |
||||||
|
import common |
||||||
|
|
||||||
|
|
||||||
|
#创建文本文件 |
||||||
|
def creatDoingLog(order_id): |
||||||
|
#在指定路径创建一条 order_id.txt 文本 |
||||||
|
file_path = f"doingLog/{order_id}.txt" |
||||||
|
if os.path.exists(file_path): |
||||||
|
return |
||||||
|
#创建 |
||||||
|
with open(file_path, "w", encoding="utf-8") as file: |
||||||
|
file.write("1") |
||||||
|
|
||||||
|
#移除指定的文本文件 |
||||||
|
def removeDoingLog(order_id): |
||||||
|
file_path = f"doingLog/{order_id}.txt" |
||||||
|
if not os.path.exists(file_path): |
||||||
|
return |
||||||
|
os.remove(file_path) |
||||||
|
|
||||||
|
#遍历文件夹,返回指定的order_id |
||||||
|
def get_order_id_by_txt(): |
||||||
|
#遍历文件夹 |
||||||
|
for file in os.listdir("doingLog"): |
||||||
|
if file != "": |
||||||
|
arrFile = file.split(".") |
||||||
|
return arrFile[0] |
||||||
|
return None |
||||||
|
|
||||||
|
def find_blender_bin_path(): |
||||||
|
if platform.system() == 'Linux': return '/home/acprint/code/blender/blender' |
||||||
|
base_path = 'C:\\Program Files\\Blender Foundation\\' |
||||||
|
if os.path.exists(base_path): |
||||||
|
for dir in os.listdir(base_path): |
||||||
|
if dir.startswith('Blender'): |
||||||
|
blender_bin_path = base_path + dir + '\\blender.exe' |
||||||
|
return f'"{blender_bin_path}"' |
||||||
|
else: |
||||||
|
print('未找到blender安装目录') |
||||||
|
exit(1) |
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=10, wait_fixed=3000) |
||||||
|
def down_obj_fromoss(pid, print_type=1, order_id=None,download_flag="print_build"): |
||||||
|
# print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品 |
||||||
|
print('开始下载obj文件...' , pid) |
||||||
|
|
||||||
|
if not order_id is None: |
||||||
|
path = os.path.join(workdir, 'print', f'{pid}_{order_id}') |
||||||
|
else: |
||||||
|
path = os.path.join(workdir, 'print', pid) |
||||||
|
if not os.path.exists(path): os.makedirs(path) |
||||||
|
|
||||||
|
# 根据前缀获取文件列表 |
||||||
|
prefix = f'objs/print/{pid}/' |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
find = False |
||||||
|
findTex1Jpg = False |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'{pid}.obj'): |
||||||
|
find = True |
||||||
|
|
||||||
|
if download_flag == "print_build": |
||||||
|
if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": |
||||||
|
continue |
||||||
|
localfile = os.path.join(path, filename) |
||||||
|
res = oss_client.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
|
||||||
|
if not find: |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'.obj'): |
||||||
|
find = True |
||||||
|
|
||||||
|
# if download_flag == "print_build": |
||||||
|
# if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": |
||||||
|
# continue |
||||||
|
localfile = os.path.join(path, filename) |
||||||
|
res = oss_client.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
break |
||||||
|
|
||||||
|
if find: |
||||||
|
os.rename(localfile, os.path.join(path,f"{pid}.obj")) |
||||||
|
#重命名文件 |
||||||
|
|
||||||
|
#判断是否有Tex1.jpg |
||||||
|
if not os.path.exists(os.path.join(path,f"{pid}Tex1.jpg")): |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'{pid}.jpg'): |
||||||
|
findTex1Jpg = True |
||||||
|
|
||||||
|
# if download_flag == "print_build": |
||||||
|
# if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": |
||||||
|
# continue |
||||||
|
localfile = os.path.join(path, filename) |
||||||
|
res = oss_client.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
break |
||||||
|
|
||||||
|
if findTex1Jpg: |
||||||
|
os.rename(localfile, os.path.join(path,f"{pid}Tex1.jpg")) |
||||||
|
#重命名文件 |
||||||
|
|
||||||
|
# for file in filelist: |
||||||
|
# filename = file.key.split('/')[-1] |
||||||
|
# if filename == '': continue |
||||||
|
# if filename.endswith(f'{pid}.obj'): |
||||||
|
# find = True |
||||||
|
# if filename.endswith('.obj'): |
||||||
|
# print('找到其他obj文件,采用这个文件来生成需要的尺寸', file) |
||||||
|
# shutil.copy(os.path.join(path, file), os.path.join(path, f'{pid}.obj')) |
||||||
|
# find = True |
||||||
|
# break |
||||||
|
if not find: |
||||||
|
print('找不到obj文件,跳过') |
||||||
|
common.notify(f"{pid}-构建打印文件,找不到obj文件,异常,跳过该任务处理") |
||||||
|
# sys.exit(1) |
||||||
|
os.system(f'python auto_convert3d.py') |
||||||
|
return |
||||||
|
|
||||||
|
# print('下载完成后静默10秒,等待文件写入完成') |
||||||
|
# time.sleep(10) |
||||||
|
#根据pid获取orderId |
||||||
|
def getPidFromOrderId(orderId): |
||||||
|
getPidFromOrderId_url = 'https://mp.api.suwa3d.com/api/printOrder/info' |
||||||
|
print(f'{getPidFromOrderId_url}?id={orderId}') |
||||||
|
res = requests.get(f'{getPidFromOrderId_url}?id={orderId}') |
||||||
|
resCode = res.json()['code'] |
||||||
|
if int(resCode) != 1000: |
||||||
|
return -1 |
||||||
|
print(res.text) |
||||||
|
return res.json()['data'] |
||||||
|
|
||||||
|
def detect_obj4print(pid, orderId): |
||||||
|
for file in os.listdir(os.path.join(workdir, 'print', f'{pid}_{orderId}')): |
||||||
|
if file.endswith('.obj') and 'x' in file: |
||||||
|
return True |
||||||
|
|
||||||
|
|
||||||
|
def restart_current_process(new_command): |
||||||
|
try: |
||||||
|
# 保存新进程的命令 |
||||||
|
command = new_command.split() |
||||||
|
|
||||||
|
# 启动新进程 |
||||||
|
new_process = subprocess.Popen(command) |
||||||
|
|
||||||
|
# 打印新进程的PID |
||||||
|
print(f"New process started with PID: {new_process.pid}") |
||||||
|
|
||||||
|
# 终止当前进程 |
||||||
|
os._exit(0) |
||||||
|
|
||||||
|
except Exception as e: |
||||||
|
print(f"An error occurred: {e}") |
||||||
|
|
||||||
|
|
||||||
|
def make3d4print_task(orderId): |
||||||
|
res = getPidFromOrderId(orderId) |
||||||
|
if res == -1: |
||||||
|
print("查询打印订单信息失败,重新开启进程") |
||||||
|
#os.system(f'python auto_convert3d.py') |
||||||
|
return |
||||||
|
pid = str(res['pid']) |
||||||
|
if pid == "88985": |
||||||
|
return |
||||||
|
if pid == "": |
||||||
|
return |
||||||
|
#创建正在处理的文本内容 |
||||||
|
creatDoingLog(orderId) |
||||||
|
|
||||||
|
print_type = res['print_type'] |
||||||
|
digital_type = res['digital_type'] # 0: 只有手办 1: 只有数字模型 2: 手办+数字模型 |
||||||
|
|
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 start', ) |
||||||
|
isFindObj = False |
||||||
|
down_obj_fromoss(pid, print_type, orderId,download_flag="print_build") |
||||||
|
# 获取程序运行当前目录 |
||||||
|
resize_py_path = os.path.join(os.getcwd(), 'blender', 'resize_model.py') |
||||||
|
print(f'{blenderbin} -b -P {resize_py_path} -- {orderId}') |
||||||
|
os.system(f'{blenderbin} -b -P {resize_py_path} -- {orderId}') |
||||||
|
if not detect_obj4print(pid, orderId): |
||||||
|
print('obj文件生成异常,退出,重新执行') |
||||||
|
restart_current_process("python auto_convert3d.py") |
||||||
|
return |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 end') |
||||||
|
|
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 处理鼻孔 start') |
||||||
|
if os.path.exists(f'{workdir}/print/{pid}_{orderId}/{pid}Tex1_old.jpg'): |
||||||
|
print('已经处理过鼻孔,跳过') |
||||||
|
else: |
||||||
|
os.system(f'python fix_nose.py {pid}_{orderId}') |
||||||
|
#上传jpg文件 |
||||||
|
oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.jpg', os.path.join(workdir, 'print', f'{pid}_{orderId}', f'{pid}Tex1.jpg')) |
||||||
|
# oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1_old.jpg', os.path.join(workdir, 'print', f'{pid}_{orderId}', f'{pid}Tex1_old.jpg')) |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 处理鼻孔 end') |
||||||
|
|
||||||
|
# print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成脚底板二维码 start') |
||||||
|
# os.system(f'{blenderbin} -b -P d:\\apps\\blender\\auto_dm.py -- {pid}_{orderId}') |
||||||
|
# print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成脚底板二维码 end') |
||||||
|
|
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 start') |
||||||
|
|
||||||
|
path = os.path.join(workdir, 'print', f'{pid}_{orderId}') |
||||||
|
|
||||||
|
|
||||||
|
# 如果指定文件夹目标文件存在,则先删除 |
||||||
|
if os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): |
||||||
|
delete_files_in_directory(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) |
||||||
|
|
||||||
|
for file in os.listdir(path): |
||||||
|
# 跳过一些不需要上传的文件 |
||||||
|
if file in [f'{pid}.png',f'{pid}_old.jpg', f'{pid}.obj', f'{pid}_decimate.glb', f'{pid}_decimate.obj', f'{pid}_decimate.mtl', f'{pid}Tex1_decimate.jpg', f'{pid}_original.obj', f'{pid}_original.mtl']: continue |
||||||
|
print("当前目录",os.path.join(path, file)) |
||||||
|
|
||||||
|
#将文件移动到指定目录 |
||||||
|
if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/')): |
||||||
|
os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/'),mode=0o777, exist_ok=True) |
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): |
||||||
|
os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/'),mode=0o777, exist_ok=True) |
||||||
|
|
||||||
|
shutil.move(os.path.join(path, file), os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) |
||||||
|
|
||||||
|
|
||||||
|
# oss_client.put_object_from_file(f'objs/print/{pid}/{file}', os.path.join(path, file)) |
||||||
|
|
||||||
|
# texture_file = os.path.join(path, f'{pid}Tex1_decimate.jpg') |
||||||
|
# if os.path.exists(texture_file): |
||||||
|
# img = Image.open(texture_file) |
||||||
|
# img = img.resize((int(img.size[0] * 0.5), int(img.size[1] * 0.5))) |
||||||
|
# img.save(texture_file, quality=90, optimize=True) |
||||||
|
# print('resize texture file to 50% success') |
||||||
|
|
||||||
|
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) |
||||||
|
|
||||||
|
shutil.rmtree(path, ignore_errors=True) |
||||||
|
|
||||||
|
print(f'{update_makeprintobj_status_url}?id={orderId}') |
||||||
|
if digital_type == 1: |
||||||
|
print('只有数字模型,不需要推送手办打印任务,仍有调用接口') |
||||||
|
|
||||||
|
res = requests.get(f'{update_makeprintobj_status_url}?id={orderId}') |
||||||
|
print('更新打印状态:', res.text) |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 end') |
||||||
|
removeDoingLog(orderId) |
||||||
|
#生成封面图片 |
||||||
|
print('小票封面图处理中....') |
||||||
|
get_preview_image.createImage(pid) |
||||||
|
#os.system(f'python get_preview_image.py {pid}') |
||||||
|
|
||||||
|
print(f"{pid}-已处理结束") |
||||||
|
|
||||||
|
|
||||||
|
def delete_files_in_directory(directory): |
||||||
|
for file_name in os.listdir(directory): |
||||||
|
file_path = os.path.join(directory, file_name) |
||||||
|
try: |
||||||
|
if os.path.isfile(file_path): |
||||||
|
os.remove(file_path) |
||||||
|
print(f"Deleted: {file_path}") |
||||||
|
except Exception as e: |
||||||
|
print(f"Error deleting {file_path}: {e}") |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# def main(r): |
||||||
|
# print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '模型生成程序 start') |
||||||
|
# while True: |
||||||
|
#构建打印文件 |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
|
||||||
|
# atexit.register(common.notify,"虚拟机,生成打印任务程序停止") |
||||||
|
AccessKeyId = 'LTAI5tSReWm8hz7dSYxxth8f' |
||||||
|
AccessKeySecret = '8ywTDF9upPAtvgXtLKALY2iMYHIxdS' |
||||||
|
Endpoint = 'oss-cn-shanghai.aliyuncs.com' |
||||||
|
Bucket = 'suwa3d-securedata' |
||||||
|
oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket) |
||||||
|
update_check_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingPlatformCheckingStatus' |
||||||
|
update_team_check_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingTeamCheckingStatus' |
||||||
|
update_status_printstatus_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/updateBuildPrintModelStatus' |
||||||
|
update_makeprintobj_status_url = 'https://mp.api.suwa3d.com/api/printOrder/updateMakePrintObjSucceed' |
||||||
|
getRepairInfo_url = 'https://repair.api.suwa3d.com/api/modelRepairOrder/teamCheckGLBInfo' |
||||||
|
update_repair_status_url = 'https://repair.api.suwa3d.com/api/modelRepairOrder/updateStatusToWaitingTeamCheckingStatus' |
||||||
|
if platform.system() == 'Windows': |
||||||
|
workdir = 'E:\\' |
||||||
|
else: |
||||||
|
workdir = '/data/datasets/' |
||||||
|
blenderbin = find_blender_bin_path() |
||||||
|
|
||||||
|
#r = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6) |
||||||
|
#E:\\complate/objs/147852_54579/ |
||||||
|
# os.remove(os.path.join(workdir, f'complate/objs/147852_54579/')) |
||||||
|
orderId = sys.argv[1] |
||||||
|
print(sys.argv) |
||||||
|
if orderId == 0: |
||||||
|
print("请输入order_id") |
||||||
|
exit() |
||||||
|
|
||||||
|
make3d4print_task(orderId) |
||||||
@ -0,0 +1,368 @@ |
|||||||
|
from ctypes import util |
||||||
|
import os, oss2, time, redis, requests, shutil, sys, subprocess, json, platform,random |
||||||
|
from PIL import Image, ImageDraw, ImageFont |
||||||
|
from retrying import retry |
||||||
|
import atexit,platform |
||||||
|
import get_preview_image |
||||||
|
# if platform.system() == 'Windows': |
||||||
|
# sys.path.append('libs\\') |
||||||
|
sys.path.append('/home/acprint/code/libs/') |
||||||
|
import common |
||||||
|
|
||||||
|
|
||||||
|
#创建文本文件 |
||||||
|
def creatDoingLog(order_id): |
||||||
|
#在指定路径创建一条 order_id.txt 文本 |
||||||
|
file_path = f"doingLog/{order_id}.txt" |
||||||
|
if os.path.exists(file_path): |
||||||
|
return |
||||||
|
#创建 |
||||||
|
with open(file_path, "w", encoding="utf-8") as file: |
||||||
|
file.write("1") |
||||||
|
|
||||||
|
#移除指定的文本文件 |
||||||
|
def removeDoingLog(order_id): |
||||||
|
file_path = f"doingLog/{order_id}.txt" |
||||||
|
if not os.path.exists(file_path): |
||||||
|
return |
||||||
|
os.remove(file_path) |
||||||
|
|
||||||
|
#遍历文件夹,返回指定的order_id |
||||||
|
def get_order_id_by_txt(): |
||||||
|
#遍历文件夹 |
||||||
|
for file in os.listdir("doingLog"): |
||||||
|
if file != "": |
||||||
|
arrFile = file.split(".") |
||||||
|
return arrFile[0] |
||||||
|
return None |
||||||
|
|
||||||
|
def find_blender_bin_path(): |
||||||
|
if platform.system() == 'Linux': return '/home/acprint/code/blender/blender' |
||||||
|
base_path = 'C:\\Program Files\\Blender Foundation\\' |
||||||
|
if os.path.exists(base_path): |
||||||
|
for dir in os.listdir(base_path): |
||||||
|
if dir.startswith('Blender'): |
||||||
|
blender_bin_path = base_path + dir + '\\blender.exe' |
||||||
|
return f'"{blender_bin_path}"' |
||||||
|
else: |
||||||
|
print('未找到blender安装目录') |
||||||
|
exit(1) |
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=10, wait_fixed=3000) |
||||||
|
def down_obj_fromoss(pid, print_type=1, order_id=None,download_flag="print_build"): |
||||||
|
# print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品 |
||||||
|
print('开始下载obj文件...' , pid) |
||||||
|
|
||||||
|
if not order_id is None: |
||||||
|
path = os.path.join(workdir, 'print', f'{pid}_{order_id}') |
||||||
|
else: |
||||||
|
path = os.path.join(workdir, 'print', pid) |
||||||
|
if not os.path.exists(path): os.makedirs(path) |
||||||
|
|
||||||
|
# 根据前缀获取文件列表 |
||||||
|
prefix = f'objs/print/{pid}/' |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
find = False |
||||||
|
findTex1Jpg = False |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'{pid}.obj'): |
||||||
|
find = True |
||||||
|
|
||||||
|
if download_flag == "print_build": |
||||||
|
if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": |
||||||
|
continue |
||||||
|
localfile = os.path.join(path, filename) |
||||||
|
res = oss_client.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
|
||||||
|
if not find: |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'.obj'): |
||||||
|
find = True |
||||||
|
|
||||||
|
# if download_flag == "print_build": |
||||||
|
# if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": |
||||||
|
# continue |
||||||
|
localfile = os.path.join(path, filename) |
||||||
|
res = oss_client.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
break |
||||||
|
|
||||||
|
if find: |
||||||
|
os.rename(localfile, os.path.join(path,f"{pid}.obj")) |
||||||
|
#重命名文件 |
||||||
|
|
||||||
|
#判断是否有Tex1.jpg |
||||||
|
if not os.path.exists(os.path.join(path,f"{pid}Tex1.jpg")): |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'{pid}.jpg'): |
||||||
|
findTex1Jpg = True |
||||||
|
|
||||||
|
# if download_flag == "print_build": |
||||||
|
# if filename != str(pid)+".obj" and filename != str(pid)+".mtl" and filename != str(pid)+"Tex1.jpg": |
||||||
|
# continue |
||||||
|
localfile = os.path.join(path, filename) |
||||||
|
res = oss_client.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
break |
||||||
|
|
||||||
|
if findTex1Jpg: |
||||||
|
os.rename(localfile, os.path.join(path,f"{pid}Tex1.jpg")) |
||||||
|
#重命名文件 |
||||||
|
|
||||||
|
# for file in filelist: |
||||||
|
# filename = file.key.split('/')[-1] |
||||||
|
# if filename == '': continue |
||||||
|
# if filename.endswith(f'{pid}.obj'): |
||||||
|
# find = True |
||||||
|
# if filename.endswith('.obj'): |
||||||
|
# print('找到其他obj文件,采用这个文件来生成需要的尺寸', file) |
||||||
|
# shutil.copy(os.path.join(path, file), os.path.join(path, f'{pid}.obj')) |
||||||
|
# find = True |
||||||
|
# break |
||||||
|
if not find: |
||||||
|
print('找不到obj文件,跳过') |
||||||
|
common.notify(f"{pid}-构建打印文件,找不到obj文件,异常,跳过该任务处理") |
||||||
|
# sys.exit(1) |
||||||
|
os.system(f'python auto_convert3d.py') |
||||||
|
return |
||||||
|
|
||||||
|
# print('下载完成后静默10秒,等待文件写入完成') |
||||||
|
# time.sleep(10) |
||||||
|
#根据pid获取orderId |
||||||
|
def getPidFromOrderId(orderId): |
||||||
|
getPidFromOrderId_url = 'https://mp.api.suwa3d.com/api/printOrder/info' |
||||||
|
print(f'{getPidFromOrderId_url}?id={orderId}') |
||||||
|
res = requests.get(f'{getPidFromOrderId_url}?id={orderId}') |
||||||
|
resCode = res.json()['code'] |
||||||
|
if int(resCode) != 1000: |
||||||
|
return -1 |
||||||
|
print(res.text) |
||||||
|
return res.json()['data'] |
||||||
|
|
||||||
|
def detect_obj4print(pid, orderId): |
||||||
|
for file in os.listdir(os.path.join(workdir, 'print', f'{pid}_{orderId}')): |
||||||
|
if file.endswith('.obj') and 'x' in file: |
||||||
|
return True |
||||||
|
|
||||||
|
|
||||||
|
def restart_current_process(new_command): |
||||||
|
try: |
||||||
|
# 保存新进程的命令 |
||||||
|
command = new_command.split() |
||||||
|
|
||||||
|
# 启动新进程 |
||||||
|
new_process = subprocess.Popen(command) |
||||||
|
|
||||||
|
# 打印新进程的PID |
||||||
|
print(f"New process started with PID: {new_process.pid}") |
||||||
|
|
||||||
|
# 终止当前进程 |
||||||
|
os._exit(0) |
||||||
|
|
||||||
|
except Exception as e: |
||||||
|
print(f"An error occurred: {e}") |
||||||
|
|
||||||
|
|
||||||
|
def make3d4print_task(r): |
||||||
|
|
||||||
|
|
||||||
|
tempOrderId = "0" |
||||||
|
try: |
||||||
|
if r.llen('model:printOrder') == 0: |
||||||
|
time.sleep(5) |
||||||
|
|
||||||
|
# tempOrderId = get_order_id_by_txt() |
||||||
|
# if tempOrderId == "0": |
||||||
|
# return |
||||||
|
except Exception as e: |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), 'redis连接异常,重新连接') |
||||||
|
print(e) |
||||||
|
time.sleep(5) |
||||||
|
r = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6) |
||||||
|
return |
||||||
|
orderId = None |
||||||
|
if tempOrderId == "0": |
||||||
|
orderId = r.lpop('model:printOrder') |
||||||
|
else: |
||||||
|
orderId = tempOrderId |
||||||
|
|
||||||
|
if orderId is None: return |
||||||
|
|
||||||
|
if type(orderId) != str: |
||||||
|
orderId = orderId.decode('utf-8') |
||||||
|
# orderId = 56077 |
||||||
|
res = getPidFromOrderId(orderId) |
||||||
|
if res == -1: |
||||||
|
print("查询打印订单信息失败,重新开启进程") |
||||||
|
#os.system(f'python auto_convert3d.py') |
||||||
|
return |
||||||
|
pid = str(res['pid']) |
||||||
|
if pid == "88985": |
||||||
|
return |
||||||
|
if pid == "": |
||||||
|
return |
||||||
|
#创建正在处理的文本内容 |
||||||
|
creatDoingLog(orderId) |
||||||
|
|
||||||
|
print_type = res['print_type'] |
||||||
|
digital_type = res['digital_type'] # 0: 只有手办 1: 只有数字模型 2: 手办+数字模型 |
||||||
|
|
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 start', ) |
||||||
|
isFindObj = False |
||||||
|
down_obj_fromoss(pid, print_type, orderId,download_flag="print_build") |
||||||
|
# 获取程序运行当前目录 |
||||||
|
resize_py_path = os.path.join(os.getcwd(), 'blender', 'resize_model.py') |
||||||
|
print(f'{blenderbin} -b -P {resize_py_path} -- {orderId}') |
||||||
|
os.system(f'{blenderbin} -b -P {resize_py_path} -- {orderId}') |
||||||
|
if not detect_obj4print(pid, orderId): |
||||||
|
print('obj文件生成异常,退出,重新执行') |
||||||
|
restart_current_process("python auto_convert3d.py") |
||||||
|
return |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 end') |
||||||
|
|
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 处理鼻孔 start') |
||||||
|
if os.path.exists(f'{workdir}/print/{pid}_{orderId}/{pid}Tex1_old.jpg'): |
||||||
|
print('已经处理过鼻孔,跳过') |
||||||
|
else: |
||||||
|
os.system(f'python fix_nose.py {pid}_{orderId}') |
||||||
|
#上传jpg文件 |
||||||
|
oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.jpg', os.path.join(workdir, 'print', f'{pid}_{orderId}', f'{pid}Tex1.jpg')) |
||||||
|
# oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1_old.jpg', os.path.join(workdir, 'print', f'{pid}_{orderId}', f'{pid}Tex1_old.jpg')) |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 处理鼻孔 end') |
||||||
|
|
||||||
|
# print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成脚底板二维码 start') |
||||||
|
# os.system(f'{blenderbin} -b -P d:\\apps\\blender\\auto_dm.py -- {pid}_{orderId}') |
||||||
|
# print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成脚底板二维码 end') |
||||||
|
|
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 start') |
||||||
|
|
||||||
|
path = os.path.join(workdir, 'print', f'{pid}_{orderId}') |
||||||
|
|
||||||
|
|
||||||
|
# 如果指定文件夹目标文件存在,则先删除 |
||||||
|
if os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): |
||||||
|
delete_files_in_directory(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) |
||||||
|
|
||||||
|
for file in os.listdir(path): |
||||||
|
# 跳过一些不需要上传的文件 |
||||||
|
if file in [f'{pid}.png',f'{pid}_old.jpg', f'{pid}.obj', f'{pid}_decimate.glb', f'{pid}_decimate.obj', f'{pid}_decimate.mtl', f'{pid}Tex1_decimate.jpg', f'{pid}_original.obj', f'{pid}_original.mtl']: continue |
||||||
|
print("当前目录",os.path.join(path, file)) |
||||||
|
|
||||||
|
#将文件移动到指定目录 |
||||||
|
if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/')): |
||||||
|
os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/'),mode=0o777, exist_ok=True) |
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')): |
||||||
|
os.makedirs(os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/'),mode=0o777, exist_ok=True) |
||||||
|
|
||||||
|
shutil.move(os.path.join(path, file), os.path.join(workdir, f'complate/objs/{pid}/order_{orderId}/')) |
||||||
|
|
||||||
|
|
||||||
|
# oss_client.put_object_from_file(f'objs/print/{pid}/{file}', os.path.join(path, file)) |
||||||
|
|
||||||
|
# texture_file = os.path.join(path, f'{pid}Tex1_decimate.jpg') |
||||||
|
# if os.path.exists(texture_file): |
||||||
|
# img = Image.open(texture_file) |
||||||
|
# img = img.resize((int(img.size[0] * 0.5), int(img.size[1] * 0.5))) |
||||||
|
# img.save(texture_file, quality=90, optimize=True) |
||||||
|
# print('resize texture file to 50% success') |
||||||
|
|
||||||
|
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) |
||||||
|
|
||||||
|
shutil.rmtree(path, ignore_errors=True) |
||||||
|
|
||||||
|
print(f'{update_makeprintobj_status_url}?id={orderId}') |
||||||
|
if digital_type == 1: |
||||||
|
print('只有数字模型,不需要推送手办打印任务,仍有调用接口') |
||||||
|
|
||||||
|
res = requests.get(f'{update_makeprintobj_status_url}?id={orderId}') |
||||||
|
print('更新打印状态:', res.text) |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 上传生成的模型 end') |
||||||
|
removeDoingLog(orderId) |
||||||
|
#生成封面图片 |
||||||
|
print('小票封面图处理中....') |
||||||
|
get_preview_image.createImage(pid) |
||||||
|
#os.system(f'python get_preview_image.py {pid}') |
||||||
|
|
||||||
|
print(f"{pid}-已处理结束") |
||||||
|
|
||||||
|
restart_current_process("python auto_convert3d.py") |
||||||
|
|
||||||
|
|
||||||
|
def delete_files_in_directory(directory): |
||||||
|
for file_name in os.listdir(directory): |
||||||
|
file_path = os.path.join(directory, file_name) |
||||||
|
try: |
||||||
|
if os.path.isfile(file_path): |
||||||
|
os.remove(file_path) |
||||||
|
print(f"Deleted: {file_path}") |
||||||
|
except Exception as e: |
||||||
|
print(f"Error deleting {file_path}: {e}") |
||||||
|
|
||||||
|
|
||||||
|
def create_redis_connection(): |
||||||
|
"""创建 Redis 连接,若连接失败则重试""" |
||||||
|
while True: |
||||||
|
try: |
||||||
|
r = redis.Redis(host="106.14.158.208", password="kcV2000", port=6379, db=6) |
||||||
|
# 尝试进行一次操作,检查连接是否有效 |
||||||
|
r.ping() # ping 操作是一个简单的连接测试 |
||||||
|
print("Redis连接成功!") |
||||||
|
return r |
||||||
|
except ConnectionError: |
||||||
|
print("Redis连接失败,正在重试...") |
||||||
|
time.sleep(5) |
||||||
|
|
||||||
|
|
||||||
|
def main(r): |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '模型生成程序 start') |
||||||
|
while True: |
||||||
|
try: |
||||||
|
#随机休眠 1- 9 |
||||||
|
time.sleep(random.uniform(1, 9)) |
||||||
|
|
||||||
|
|
||||||
|
#构建打印文件 |
||||||
|
make3d4print_task(r) |
||||||
|
except Exception as e: |
||||||
|
print(f'出现异常:{e}') |
||||||
|
time.sleep(15) |
||||||
|
r = create_redis_connection() |
||||||
|
continue |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
|
||||||
|
# atexit.register(common.notify,"虚拟机,生成打印任务程序停止") |
||||||
|
AccessKeyId = 'LTAI5tSReWm8hz7dSYxxth8f' |
||||||
|
AccessKeySecret = '8ywTDF9upPAtvgXtLKALY2iMYHIxdS' |
||||||
|
Endpoint = 'oss-cn-shanghai.aliyuncs.com' |
||||||
|
Bucket = 'suwa3d-securedata' |
||||||
|
oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket) |
||||||
|
update_check_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingPlatformCheckingStatus' |
||||||
|
update_team_check_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingTeamCheckingStatus' |
||||||
|
update_status_printstatus_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/updateBuildPrintModelStatus' |
||||||
|
update_makeprintobj_status_url = 'https://mp.api.suwa3d.com/api/printOrder/updateMakePrintObjSucceed' |
||||||
|
getRepairInfo_url = 'https://repair.api.suwa3d.com/api/modelRepairOrder/teamCheckGLBInfo' |
||||||
|
update_repair_status_url = 'https://repair.api.suwa3d.com/api/modelRepairOrder/updateStatusToWaitingTeamCheckingStatus' |
||||||
|
if platform.system() == 'Windows': |
||||||
|
workdir = 'E:\\' |
||||||
|
else: |
||||||
|
workdir = '/data/datasets/' |
||||||
|
blenderbin = find_blender_bin_path() |
||||||
|
|
||||||
|
r = create_redis_connection() |
||||||
|
#E:\\complate/objs/147852_54579/ |
||||||
|
# os.remove(os.path.join(workdir, f'complate/objs/147852_54579/')) |
||||||
|
main(r) |
||||||
|
After Width: | Height: | Size: 3.4 MiB |
@ -0,0 +1,148 @@ |
|||||||
|
import bpy, sys, os, math, bmesh |
||||||
|
# from PIL import Image, ImageDraw, ImageFont |
||||||
|
|
||||||
|
def gen_qrcode(pid): |
||||||
|
fontHeightMax = 40 |
||||||
|
fontsize = 1 |
||||||
|
qr = qrcode.QRCode() |
||||||
|
qr.border = 2 |
||||||
|
qr.add_data(pid) |
||||||
|
img = qr.make_image(fit=True) |
||||||
|
img = img.transform((250, 294), Image.Transform.EXTENT, (0, 0, 250, 294), fillcolor='white') |
||||||
|
|
||||||
|
cwd = os.path.dirname(os.path.abspath(__file__)) |
||||||
|
fontfile = os.path.join(cwd, 'fonts', 'Helvetica.ttf') |
||||||
|
font = ImageFont.truetype(fontfile, fontsize) |
||||||
|
while font.getsize(pid)[1] <= fontHeightMax and font.getsize(pid)[0] <= 240: |
||||||
|
fontsize += 1 |
||||||
|
font = ImageFont.truetype(fontfile, fontsize) |
||||||
|
fontsize -= 1 |
||||||
|
|
||||||
|
captionx = (250 - font.getsize(pid)[0]) / 2 |
||||||
|
draw = ImageDraw.Draw(img) |
||||||
|
draw.text((captionx, 242), pid, font=font) |
||||||
|
img.show() |
||||||
|
img.save(f'{workdir}{pid}.png') |
||||||
|
|
||||||
|
def auto_rotate(pid): |
||||||
|
# 坐标复位 |
||||||
|
obj = bpy.context.selected_objects[0] |
||||||
|
obj.rotation_euler[0] = 0 |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') |
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Y', 'Z'}) |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
# bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}_align_yz.obj') |
||||||
|
|
||||||
|
# 躺平到打印机排版需要的坐标与角度 |
||||||
|
obj.rotation_euler = (math.radians(90), math.radians(90), 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
# bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}_rotate_y90.obj') |
||||||
|
|
||||||
|
heights = {} |
||||||
|
min_height = 999999 |
||||||
|
min_i = 0 |
||||||
|
max_height = -999999 |
||||||
|
max_i = 0 |
||||||
|
|
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') |
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_3', align_axis={'X', 'Y', 'Z'}) |
||||||
|
|
||||||
|
# 步进精度2旋转X轴到180度,找到Y轴最低点和最高点,其中最低点为打印 |
||||||
|
step = 2 |
||||||
|
i = 0 |
||||||
|
while i <= 180: |
||||||
|
obj.rotation_euler = (math.radians(step), 0, 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
if obj.dimensions[1] < min_height: |
||||||
|
min_height = obj.dimensions[1] |
||||||
|
min_i = i |
||||||
|
if obj.dimensions[1] > max_height: |
||||||
|
max_height = obj.dimensions[1] |
||||||
|
max_i = i |
||||||
|
heights[i] = (obj.dimensions[0], obj.dimensions[1], obj.dimensions[2]) |
||||||
|
print(i, heights[i]) |
||||||
|
i += step |
||||||
|
|
||||||
|
obj.rotation_euler = (0, 0, 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
obj.rotation_euler = (math.radians(min_i), 0, 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}.obj') |
||||||
|
|
||||||
|
# obj.rotation_euler = (0, 0, 0) |
||||||
|
# bpy.ops.object.transform_apply(rotation=True) |
||||||
|
# obj.rotation_euler = (math.radians(max_i), 0, 0) |
||||||
|
# bpy.ops.object.transform_apply(rotation=True) |
||||||
|
# bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}_maxz.obj') |
||||||
|
print(f'最小高度: {min_height} @ {heights[min_i]}min_i:{min_i}' , f'最大高度: {max_height} @ {heights[max_i]}max_i:{max_i}') |
||||||
|
|
||||||
|
def cut_obj(pid): |
||||||
|
# 根据定位用一个面切割模型 |
||||||
|
offset = 45.5 |
||||||
|
radian = math.radians(90) |
||||||
|
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(offset, 0, 0), rotation=(0, radian, 0), scale=(1, 1, 1)) |
||||||
|
|
||||||
|
# 布尔切割,保留交集切面 |
||||||
|
bpy.ops.object.modifier_add(type='BOOLEAN') |
||||||
|
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[pid] |
||||||
|
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT' |
||||||
|
bpy.context.object.modifiers["Boolean"].solver = 'FAST' |
||||||
|
bpy.ops.object.modifier_apply(modifier="Boolean") |
||||||
|
|
||||||
|
# 拆分切割面为多个多边形,然后遍历多边形,找到最大的面积 |
||||||
|
bpy.ops.mesh.separate(type='LOOSE') |
||||||
|
|
||||||
|
max_area = 0 |
||||||
|
max_obj = None |
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.type == 'MESH' and obj.name.startswith('Plane'): |
||||||
|
area = obj.data.polygons[0].area |
||||||
|
if area > max_area: |
||||||
|
max_area = area |
||||||
|
max_obj = obj |
||||||
|
|
||||||
|
# 选中最大面积的多边形,然后计算中心点 |
||||||
|
bpy.ops.object.select_all(action='DESELECT') |
||||||
|
max_obj.select_set(True) |
||||||
|
bpy.context.view_layer.objects.active = max_obj |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') |
||||||
|
|
||||||
|
return max_obj |
||||||
|
|
||||||
|
def main(): |
||||||
|
filename = f'{workdir}{pid}.obj' |
||||||
|
print('正在处理:', filename) |
||||||
|
bpy.ops.import_scene.obj(filepath=filename) |
||||||
|
|
||||||
|
auto_rotate(pid) |
||||||
|
# gen_qrcode(pid) |
||||||
|
|
||||||
|
# 脚底切片,查找最大面积,计算中心点,计算坐标位置,怼入二维码贴片 |
||||||
|
max_obj = cut_obj(pid) |
||||||
|
bpy.ops.import_scene.obj(filepath=f'{workdir}qr.obj') |
||||||
|
qr_obj = bpy.data.objects['Cube'] |
||||||
|
shore_obj = bpy.data.objects['Cube.001'] |
||||||
|
# bpy.data.objects['Cube'].origin_set(type='ORIGIN_GEOMETRY') |
||||||
|
# bpy.data.objects['Cube.001'].origin_set(type='ORIGIN_GEOMETRY') |
||||||
|
# bpy.data.objects['Cube.002'].origin_set(type='ORIGIN_GEOMETRY') |
||||||
|
# bpy.data.objects['Cube.003'].origin_set(type='ORIGIN_GEOMETRY') |
||||||
|
bpy.data.objects['Cube'] = (math.radians(90), math.radians(90), 0) |
||||||
|
bpy.data.objects['Cube.001'].rotation_euler = (math.radians(90), math.radians(90), 0) |
||||||
|
bpy.data.objects['Cube.002'].rotation_euler = (math.radians(90), math.radians(90), 0) |
||||||
|
bpy.data.objects['Cube.003'].rotation_euler = (math.radians(90), math.radians(90), 0) |
||||||
|
qr_obj.location = (max_obj.location[0] - qr_obj.dimensions[1] / 2 - shore_obj.dimensions[1]/2, max_obj.location[1], max_obj.location[2]) |
||||||
|
shore_obj.location = (qr_obj.location[0] - shore_obj.dimensions[1]/2, max_obj.location[1], max_obj.location[2]) |
||||||
|
bpy.data.objects['Cube.002'].location = (shore_obj.location[0], shore_obj.location[1]+0.2, shore_obj.location[2]) |
||||||
|
bpy.data.objects['Cube.003'].location = (shore_obj.location[0], shore_obj.location[1]-0.2, shore_obj.location[2]) |
||||||
|
|
||||||
|
bpy.ops.object.transform_apply(rotation=True, location=True, scale=True) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
workdir = '/home/water/Downloads/' |
||||||
|
if len(sys.argv) - (sys.argv.index("--") +1) < 1: |
||||||
|
print("Usage: blender -b -P auto_qrcode.py -- <pid>") |
||||||
|
sys.exit(1) |
||||||
|
pid = sys.argv[sys.argv.index("--") + 1] |
||||||
|
main() |
||||||
@ -0,0 +1,160 @@ |
|||||||
|
from math import radians |
||||||
|
import sys, os, time, bpy, requests, json, bmesh |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
import platform |
||||||
|
if platform.system() == 'Windows': |
||||||
|
sys.path.append('e:\\libs\\') |
||||||
|
else: |
||||||
|
sys.path.append('/data/deploy/make3d/make2/libs/') |
||||||
|
import config |
||||||
|
|
||||||
|
def get_obj_version(filename): |
||||||
|
with open(filename, 'r') as f: |
||||||
|
for line in f: |
||||||
|
if line.startswith('# Engine version'): |
||||||
|
return float(line.split(' ')[-1][1:].strip()[:3]) |
||||||
|
exit(0) |
||||||
|
return None |
||||||
|
|
||||||
|
def delete_lines_in_file(filename, count): |
||||||
|
with open(filename, 'r') as f: |
||||||
|
lines = f.readlines() |
||||||
|
lines = lines[count:] |
||||||
|
with open(filename, 'w') as f: |
||||||
|
f.writelines(lines) |
||||||
|
|
||||||
|
def diff_minutes_and_seconds(start): |
||||||
|
hours = int((time.time() - start) / 3600) |
||||||
|
minutes = int((time.time() - start) / 60) |
||||||
|
seconds = int((time.time() - start) % 60) |
||||||
|
microseconds = int(int((time.time() - start) * 1000000) % 1000000 / 1000) |
||||||
|
return f'{hours}:{minutes}:{seconds}.{microseconds}' |
||||||
|
|
||||||
|
def get_headcount(pid): |
||||||
|
res = requests.get(config.urls['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'] |
||||||
|
|
||||||
|
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: |
||||||
|
import bpy |
||||||
|
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 getPSid(pid): |
||||||
|
get_psid_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/photoStudio' |
||||||
|
res = requests.get(get_psid_url, params={'pid': pid}) |
||||||
|
res = json.loads(res.text) |
||||||
|
return str(res['data']) |
||||||
|
|
||||||
|
def getPSRotation(pid): |
||||||
|
get_ps_rotation_url = 'https://mp.api.suwa3d.com/api/takephotoOrder/angle' |
||||||
|
res = requests.get(get_ps_rotation_url, params={'pid': pid}) |
||||||
|
res = json.loads(res.text) |
||||||
|
rotation = (radians(0), radians(0), radians(int(res['data']))) |
||||||
|
return rotation |
||||||
|
|
||||||
|
def main(): |
||||||
|
start = time.time() |
||||||
|
workdir = 'e:\\' |
||||||
|
|
||||||
|
if len(sys.argv) - (sys.argv.index("--") +1) < 1: |
||||||
|
print("Usage: blender -b -P autofix.py -- <pids>") |
||||||
|
sys.exit(1) |
||||||
|
input_file = sys.argv[sys.argv.index("--") + 1] |
||||||
|
|
||||||
|
for pid in input_file.split(','): |
||||||
|
psid = getPSid(pid) |
||||||
|
|
||||||
|
bpy.ops.wm.read_homefile() |
||||||
|
# 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.object.delete(use_global=False, confirm=False) |
||||||
|
|
||||||
|
filename = f'{workdir}{pid}\\output\{pid}.obj' |
||||||
|
print('正在处理:', filename) |
||||||
|
bpy.ops.import_scene.obj(filepath=filename) |
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_2', align_axis={'Z'}) |
||||||
|
print('import obj time:', diff_minutes_and_seconds(start)) |
||||||
|
|
||||||
|
# rotate obj |
||||||
|
obj = bpy.context.selected_objects[0] |
||||||
|
bpy.context.view_layer.objects.active = obj |
||||||
|
obj.select_set(True) |
||||||
|
rotation = getPSRotation(pid) |
||||||
|
print('rotation:', rotation) |
||||||
|
obj.rotation_euler = rotation |
||||||
|
print('rotate obj time:', diff_minutes_and_seconds(start)) |
||||||
|
# resize object |
||||||
|
scale = 90 / obj.dimensions.z |
||||||
|
obj.scale = (scale, scale, scale) |
||||||
|
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.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}\\output\{pid}_4.blend') |
||||||
|
|
||||||
|
bm = bmesh_copy_from_object(obj) |
||||||
|
obj_volume = round(bm.calc_volume() / 1000, 3) |
||||||
|
print('volume:', obj_volume) |
||||||
|
print('weight:', obj_volume * 1.2, 'g') |
||||||
|
|
||||||
|
faces = len(obj.data.polygons) |
||||||
|
print('faces:', faces) |
||||||
|
|
||||||
|
# save object |
||||||
|
bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}\\output\{pid}.obj') |
||||||
|
|
||||||
|
# 生成数字模型 |
||||||
|
headcount = get_headcount(pid) |
||||||
|
faces_dest = 120000 * 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.export_scene.gltf(filepath=os.path.join(workdir, pid, 'output', f'{pid}_decimate.glb'), export_format='GLB', export_apply=True, export_jpeg_quality=75, export_draco_mesh_compression_enable=False) |
||||||
|
|
||||||
|
config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', os.path.join(workdir, pid, 'output', f'{pid}_decimate.glb')) |
||||||
|
print('免费体验3d相册已生成,上传glb文件:', f'glbs/3d/{pid}.glb 完成') |
||||||
|
# render scene to a file |
||||||
|
# bpy.context.scene.render.filepath = f'{workdir}{pid}_fixed.png' |
||||||
|
# bpy.ops.render.render(write_still=True, use_viewport=True) |
||||||
|
print('render time:', diff_minutes_and_seconds(start)) |
||||||
|
|
||||||
|
bpy.ops.wm.quit_blender() |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
||||||
@ -0,0 +1,144 @@ |
|||||||
|
from math import radians |
||||||
|
import sys, os, time, bpy, requests, json, bmesh |
||||||
|
|
||||||
|
def get_obj_version(filename): |
||||||
|
with open(filename, 'r') as f: |
||||||
|
for line in f: |
||||||
|
if line.startswith('# Engine version'): |
||||||
|
return float(line.split(' ')[-1][1:].strip()[:3]) |
||||||
|
exit(0) |
||||||
|
return None |
||||||
|
|
||||||
|
def delete_lines_in_file(filename, count): |
||||||
|
with open(filename, 'r') as f: |
||||||
|
lines = f.readlines() |
||||||
|
lines = lines[count:] |
||||||
|
with open(filename, 'w') as f: |
||||||
|
f.writelines(lines) |
||||||
|
|
||||||
|
def diff_minutes_and_seconds(start): |
||||||
|
hours = int((time.time() - start) / 3600) |
||||||
|
minutes = int((time.time() - start) / 60) |
||||||
|
seconds = int((time.time() - start) % 60) |
||||||
|
microseconds = int(int((time.time() - start) * 1000000) % 1000000 / 1000) |
||||||
|
return f'{hours}:{minutes}:{seconds}.{microseconds}' |
||||||
|
|
||||||
|
def getPSid(pid): |
||||||
|
get_psid_url = 'https://mp.api.suwa3d.com/api/customerP3dLog/photoStudio' |
||||||
|
res = requests.get(get_psid_url, params={'pid': pid}) |
||||||
|
res = json.loads(res.text) |
||||||
|
return str(res['data']) |
||||||
|
|
||||||
|
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: |
||||||
|
import bpy |
||||||
|
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 main(): |
||||||
|
start = time.time() |
||||||
|
config = { |
||||||
|
'0': { |
||||||
|
'rotation': (radians(0), radians(0), radians(0)), |
||||||
|
}, |
||||||
|
'1': { |
||||||
|
'rotation': (radians(0), radians(0), radians(66)), |
||||||
|
}, |
||||||
|
'29': { |
||||||
|
'rotation': (radians(0), radians(0), radians(180)), |
||||||
|
}, |
||||||
|
'45': { |
||||||
|
'rotation': (radians(0), radians(0), radians(105)), |
||||||
|
}, |
||||||
|
'46': { |
||||||
|
'rotation': (radians(0), radians(0), radians(-10)), |
||||||
|
}, |
||||||
|
'74': { |
||||||
|
'rotation': (radians(0), radians(0), radians(110)), |
||||||
|
}, |
||||||
|
'75': { |
||||||
|
'rotation': (radians(0), radians(0), radians(210)), |
||||||
|
}, |
||||||
|
} |
||||||
|
workdir = 'e:\\' |
||||||
|
|
||||||
|
if len(sys.argv) - (sys.argv.index("--") +1) < 1: |
||||||
|
print("Usage: blender -b -P autofix.py -- <pids>") |
||||||
|
sys.exit(1) |
||||||
|
input_file = sys.argv[sys.argv.index("--") + 1] |
||||||
|
|
||||||
|
for pid in input_file.split(','): |
||||||
|
psid = getPSid(pid) |
||||||
|
|
||||||
|
bpy.ops.object.delete(use_global=False, confirm=False) |
||||||
|
|
||||||
|
filename = f'{workdir}{pid}\\output\{pid}.obj' |
||||||
|
print('正在处理:', filename) |
||||||
|
bpy.ops.import_scene.obj(filepath=filename) |
||||||
|
bpy.ops.object.align(relative_to='OPT_1', align_axis={'X'}) |
||||||
|
# bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
print('import obj time:', diff_minutes_and_seconds(start)) |
||||||
|
|
||||||
|
# rotate obj |
||||||
|
obj = bpy.context.selected_objects[0] |
||||||
|
bpy.context.view_layer.objects.active = obj |
||||||
|
obj.select_set(True) |
||||||
|
|
||||||
|
if psid in config: |
||||||
|
obj.rotation_euler = config[psid]['rotation'] |
||||||
|
else: |
||||||
|
obj.rotation_euler = config['0']['rotation'] |
||||||
|
print('rotate obj time:', diff_minutes_and_seconds(start)) |
||||||
|
# bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
|
||||||
|
# resize object |
||||||
|
obj_scale = 90 / obj.dimensions.z |
||||||
|
print('scale:', obj_scale) |
||||||
|
|
||||||
|
obj.scale = (obj_scale, obj_scale, obj_scale) |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
|
||||||
|
bm = bmesh_copy_from_object(obj) |
||||||
|
obj_volume = round(bm.calc_volume() / 1000, 3) |
||||||
|
print('volume:', obj_volume) |
||||||
|
print('weight:', obj_volume * 1.2, 'g') |
||||||
|
|
||||||
|
faces = len(obj.data.polygons) |
||||||
|
print('faces:', faces) |
||||||
|
|
||||||
|
# save object |
||||||
|
bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}\\output\{pid}.obj') |
||||||
|
# render scene to a file |
||||||
|
# bpy.context.scene.render.filepath = f'{workdir}{pid}_fixed.png' |
||||||
|
# bpy.ops.render.render(write_still=True, use_viewport=True) |
||||||
|
print('render time:', diff_minutes_and_seconds(start)) |
||||||
|
|
||||||
|
bpy.ops.wm.quit_blender() |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
||||||
Binary file not shown.
@ -0,0 +1,12 @@ |
|||||||
|
#!/bin/sh |
||||||
|
BF_DIST_BIN=$(dirname "$0") |
||||||
|
BF_PROGRAM="blender" |
||||||
|
|
||||||
|
# Add own lib folder first, because Steam or other environments may set an |
||||||
|
# LD_LIBRARY_PATH that has priority over the runpath in the Blender excutable, |
||||||
|
# but contains incompatible libraries. |
||||||
|
LD_LIBRARY_PATH=${BF_DIST_BIN}/lib:${LD_LIBRARY_PATH} |
||||||
|
|
||||||
|
export LD_LIBRARY_PATH |
||||||
|
|
||||||
|
exec "$BF_DIST_BIN/$BF_PROGRAM" ${1+"$@"} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
#!/bin/sh |
||||||
|
BF_DIST_BIN=$(dirname "$0") |
||||||
|
BF_PROGRAM="blender" # BF_PROGRAM=$(basename "$0")-bin |
||||||
|
|
||||||
|
LD_LIBRARY_PATH=${BF_DIST_BIN}/lib/mesa:${LD_LIBRARY_PATH} |
||||||
|
|
||||||
|
if [ -n "$LD_LIBRARYN32_PATH" ]; then |
||||||
|
LD_LIBRARYN32_PATH=${BF_DIST_BIN}/lib/mesa:${LD_LIBRARYN32_PATH} |
||||||
|
fi |
||||||
|
if [ -n "$LD_LIBRARYN64_PATH" ]; then |
||||||
|
LD_LIBRARYN64_PATH=${BF_DIST_BIN}/lib/mesa:${LD_LIBRARYN64_PATH} |
||||||
|
fi |
||||||
|
if [ -n "$LD_LIBRARY_PATH_64" ]; then |
||||||
|
LD_LIBRARY_PATH_64=${BF_DIST_BIN}/lib/mesa:${LD_LIBRARY_PATH_64} |
||||||
|
fi |
||||||
|
|
||||||
|
# Workaround for half-transparent windows when compiz is enabled |
||||||
|
XLIB_SKIP_ARGB_VISUALS=1 |
||||||
|
|
||||||
|
export LD_LIBRARY_PATH LD_LIBRARYN32_PATH LD_LIBRARYN64_PATH LD_LIBRARY_PATH_64 LD_PRELOAD XLIB_SKIP_ARGB_VISUALS |
||||||
|
|
||||||
|
exec "$BF_DIST_BIN/$BF_PROGRAM" ${1+"$@"} |
||||||
Binary file not shown.
@ -0,0 +1,678 @@ |
|||||||
|
import os, sys, bpy, math, time, platform, cairosvg, ppf.datamatrix, shutil, requests, json, redis, oss2, heapq |
||||||
|
import matplotlib.pyplot as plt |
||||||
|
from PIL import Image |
||||||
|
import numpy as np |
||||||
|
from addon_utils import enable |
||||||
|
enable('io_import_images_as_planes') |
||||||
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
||||||
|
sys.path.append('../libs\\') |
||||||
|
import config |
||||||
|
|
||||||
|
def gen_data_matrix(print_id, qr_path, size = 300): |
||||||
|
svg = ppf.datamatrix.DataMatrix(f'p{print_id}').svg() |
||||||
|
cairosvg.svg2png(bytestring=svg, write_to=qr_path, output_width=size, output_height=size, background_color='white') |
||||||
|
|
||||||
|
def active_object(obj): |
||||||
|
bpy.context.view_layer.objects.active = obj |
||||||
|
obj.select_set(True) |
||||||
|
|
||||||
|
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(config.oss_bucket, 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 = config.oss_bucket.get_object_to_file(file.key, localfile) |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
|
||||||
|
if not find: |
||||||
|
for file in os.listdir(path): |
||||||
|
if file.endswith('.obj'): |
||||||
|
print('找到其他obj文件,采用这个文件来生成需要的尺寸', file) |
||||||
|
shutil.copy(os.path.join(path, file), os.path.join(path, f'{pid}.obj')) |
||||||
|
find = True |
||||||
|
break |
||||||
|
if not find: |
||||||
|
print('找不到obj文件,异常退出') |
||||||
|
sys.exit(1) |
||||||
|
|
||||||
|
def find_obj(pid, order_id): |
||||||
|
find = False |
||||||
|
if not os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl')): |
||||||
|
print('没有找到obj模型文件,开始下载') |
||||||
|
down_obj_fromoss(pid, order_id=order_id) |
||||||
|
if os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg')): |
||||||
|
shutil.move(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg'), os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg')) |
||||||
|
with open(os.path.join(workdir, f'{pid}_{order_id}', 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, f'{pid}_{order_id}', f'{pid}.mtl'), 'w') as f: |
||||||
|
f.writelines(lines) |
||||||
|
|
||||||
|
filelist = os.listdir(os.path.join(workdir, f'{pid}_{order_id}')) |
||||||
|
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 find_pid_objname(pid): |
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.name.startswith(str(pid)): |
||||||
|
return obj.name |
||||||
|
|
||||||
|
def get_obj_max_foot(): |
||||||
|
filename = find_obj(pid, order_id) |
||||||
|
|
||||||
|
filename = os.path.join(workdir, f'{pid}_{order_id}', filename) |
||||||
|
bpy.ops.wm.read_homefile() |
||||||
|
bpy.context.preferences.view.language = 'en_US' |
||||||
|
bpy.ops.object.delete(use_global=False, confirm=False) |
||||||
|
bpy.ops.import_scene.obj(filepath=filename) |
||||||
|
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' |
||||||
|
|
||||||
|
obj = bpy.context.selected_objects[0] |
||||||
|
bpy.context.view_layer.objects.active = obj |
||||||
|
obj.select_set(True) |
||||||
|
|
||||||
|
pid_objname = find_pid_objname(pid) |
||||||
|
|
||||||
|
scale = 90 / obj.dimensions.y |
||||||
|
obj.scale = (scale, scale, scale) |
||||||
|
|
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'}) |
||||||
|
|
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
obj.location[0] = 0 |
||||||
|
obj.location[1] = 0 |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
|
||||||
|
if pid in ('76461', '98871', '112139'): |
||||||
|
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(0, 0, 0.6), scale=(1, 1, 1)) |
||||||
|
else: |
||||||
|
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(0, 0, 0.2), scale=(1, 1, 1)) |
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(workdir, f'{pid}_{order_id}', f'{pid}_{order_id}.blend')) |
||||||
|
bpy.ops.object.modifier_add(type='BOOLEAN') |
||||||
|
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[pid_objname] |
||||||
|
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT' |
||||||
|
bpy.context.object.modifiers["Boolean"].solver = 'FAST' |
||||||
|
bpy.ops.object.modifier_apply(modifier="Boolean") |
||||||
|
|
||||||
|
bpy.ops.mesh.separate(type='LOOSE') |
||||||
|
|
||||||
|
max_area = 0 |
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.type == 'MESH' and obj.name.startswith('Plane'): |
||||||
|
if len(obj.data.polygons) == 0: continue |
||||||
|
area = obj.data.polygons[0].area |
||||||
|
if area > max_area: |
||||||
|
max_area = area |
||||||
|
obj.name = 'foot' |
||||||
|
print(f'最大脚底板面积: {max_area} cm²') |
||||||
|
if max_area < 5: |
||||||
|
print('最大脚底板面积太小,脚底模型可能有破损,进行再次处理') |
||||||
|
numsTemp = 0 |
||||||
|
#最多执行三次 重新处理 |
||||||
|
while numsTemp < 3: |
||||||
|
#每次削的比例要加上去 |
||||||
|
tempArea = 0.2+numsTemp*0.2 |
||||||
|
max_area = check_and_deal_foot_area(tempArea,pid_objname) |
||||||
|
if max_area >= 5: |
||||||
|
break |
||||||
|
numsTemp += 1 |
||||||
|
|
||||||
|
if max_area < 8: |
||||||
|
print('最大脚底板面积处理多次还没有得到理想的面积,退出') |
||||||
|
#移除该脚底板的面积处理 |
||||||
|
res = requests.get(f'https://mp.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') |
||||||
|
os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
return |
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=os.path.join(workdir, f'{pid}_{order_id}', f'{pid}_{order_id}.blend')) |
||||||
|
active_object(bpy.data.objects['foot']) |
||||||
|
foot_points = get_plane_points(bpy.data.objects['foot']) |
||||||
|
# plot(get_plane_points(bpy.data.objects['foot']), 'blue') |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
|
||||||
|
# print(f"location: {bpy.data.objects['foot'].location}") |
||||||
|
bpy.ops.import_image.to_plane(files=[{"name":"qr.png"}], directory=f"{workdir}{pid}_{order_id}", relative=False) |
||||||
|
# bpy.ops.mesh.primitive_plane_add(size=1, enter_editmode=False, align='WORLD', location=(0, 0, 0), scale=(1, 1, 1)) |
||||||
|
|
||||||
|
# print(f"new_location: {bpy.data.objects['foot'].location}") |
||||||
|
|
||||||
|
active_object(bpy.data.objects['qr']) |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
bpy.data.objects['qr'].rotation_euler[0] = 0 |
||||||
|
bpy.data.objects['qr'].location = bpy.data.objects['foot'].location |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
# bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
# print(f"qr_location: {bpy.data.objects['qr'].location}") |
||||||
|
# print(f'qr_points: {get_plane_points(bpy.data.objects["qr"])}') |
||||||
|
# plot(get_plane_points(bpy.data.objects['qr']), 'red') |
||||||
|
return foot_points |
||||||
|
|
||||||
|
#检测到脚底板面积小于5cm²,重新调几次程序再次处理,如果还不行就退出 |
||||||
|
def check_and_deal_foot_area(tempArea,pid_objname): |
||||||
|
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(0, 0, tempArea), scale=(1, 1, 1)) |
||||||
|
bpy.ops.object.modifier_add(type='BOOLEAN') |
||||||
|
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[pid_objname] |
||||||
|
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT' |
||||||
|
bpy.context.object.modifiers["Boolean"].solver = 'FAST' |
||||||
|
bpy.ops.object.modifier_apply(modifier="Boolean") |
||||||
|
|
||||||
|
bpy.ops.mesh.separate(type='LOOSE') |
||||||
|
|
||||||
|
max_area = 0 |
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.type == 'MESH' and obj.name.startswith('Plane'): |
||||||
|
if len(obj.data.polygons) == 0: continue |
||||||
|
area = obj.data.polygons[0].area |
||||||
|
if area > max_area: |
||||||
|
max_area = area |
||||||
|
obj.name = 'foot' |
||||||
|
print("再次处理脚底板得到的面积:" + str(max_area) + "cm²") |
||||||
|
return max_area |
||||||
|
|
||||||
|
|
||||||
|
def euclidean_distance(p1, p2): |
||||||
|
return ((p1[0] - p2[0]) ** 2 + (p1[1] - p2[1]) ** 2) ** 0.5 |
||||||
|
|
||||||
|
def nearest_neighbor_sort(points): |
||||||
|
print('nearest neighbor sort') |
||||||
|
n = len(points) |
||||||
|
visited = set() |
||||||
|
sorted_points = [] |
||||||
|
i = 1 |
||||||
|
# Start from the first point |
||||||
|
current_point = points[0] |
||||||
|
|
||||||
|
#while len(visited) < n: |
||||||
|
while i < n: |
||||||
|
i += 1 |
||||||
|
sorted_points.append(current_point) |
||||||
|
visited.add(current_point) |
||||||
|
|
||||||
|
# Create a priority queue to store distances and points |
||||||
|
distance_queue = [] |
||||||
|
for point in points: |
||||||
|
if point not in visited: |
||||||
|
distance = euclidean_distance(current_point, point) |
||||||
|
heapq.heappush(distance_queue, (distance, point)) |
||||||
|
|
||||||
|
# Find the nearest unvisited point |
||||||
|
while distance_queue: |
||||||
|
distance, next_point = heapq.heappop(distance_queue) |
||||||
|
if next_point not in visited: |
||||||
|
current_point = next_point |
||||||
|
break |
||||||
|
|
||||||
|
|
||||||
|
return sorted_points |
||||||
|
|
||||||
|
def get_max_qr(foot_points): |
||||||
|
|
||||||
|
def dis_flag(square, foot_points): |
||||||
|
for point in foot_points: |
||||||
|
dis0 = get_distance_from_point_to_line(point, square[0], square[1]) |
||||||
|
dis1 = get_distance_from_point_to_line(point, square[1], square[2]) |
||||||
|
dis2 = get_distance_from_point_to_line(point, square[2], square[3]) |
||||||
|
dis3 = get_distance_from_point_to_line(point, square[3], square[0]) |
||||||
|
min_dis = min([dis0, dis1, dis2, dis3]) |
||||||
|
return min_dis > 0.5 |
||||||
|
|
||||||
|
def get_distance_from_point_to_line(point, line_point1, line_point2): |
||||||
|
# 对于两点坐标为同一点时,返回点与点的距离 |
||||||
|
if line_point1 == line_point2: |
||||||
|
point_array = np.array(point) |
||||||
|
point1_array = np.array(line_point1) |
||||||
|
return np.linalg.norm(point_array - point1_array) |
||||||
|
# 计算直线的三个参数 |
||||||
|
A = line_point2[1] - line_point1[1] |
||||||
|
B = line_point1[0] - line_point2[0] |
||||||
|
C = (line_point1[1] - line_point2[1]) * line_point1[0] + \ |
||||||
|
(line_point2[0] - line_point1[0]) * line_point1[1] |
||||||
|
# 根据点到直线的距离公式计算距离 |
||||||
|
distance = np.abs(A * point[0] + B * point[1] + C) / (np.sqrt(A ** 2 + B ** 2)) |
||||||
|
return distance |
||||||
|
|
||||||
|
# 判断方形是否在轮廓内 |
||||||
|
def square_in_polygon_default(square, polygon): |
||||||
|
for point in square: |
||||||
|
if not point_in_polygon(point, polygon): |
||||||
|
return False |
||||||
|
return True |
||||||
|
|
||||||
|
# 自定义二维码初始坐标 |
||||||
|
def get_default_qr_points(foot_points): |
||||||
|
max_x = max([x[0] for x in foot_points]) |
||||||
|
min_x = min([x[0] for x in foot_points]) |
||||||
|
max_y = max([x[1] for x in foot_points]) |
||||||
|
min_y = min([x[1] for x in foot_points]) |
||||||
|
|
||||||
|
center_x, center_y = (max_x + min_x) / 2, (max_y + min_y) / 2 |
||||||
|
flag_default = point_in_polygon((center_x, center_y), foot_points) |
||||||
|
if not flag_default: |
||||||
|
index_move = 0 |
||||||
|
while not flag_default and index_move < 5: |
||||||
|
center_x = (center_x + min_x) / 2 |
||||||
|
index_move += 1 |
||||||
|
flag_default = point_in_polygon((center_x, center_y), foot_points) |
||||||
|
if not flag_default: |
||||||
|
while not flag_default: |
||||||
|
center_y = (center_y + min_y) / 2 |
||||||
|
flag_default = point_in_polygon((center_x, center_y), foot_points) |
||||||
|
|
||||||
|
length = min((center_x - min_x) / 2, (center_y - min_y) / 2) / 2 |
||||||
|
# 在不规则平面中心位置初始化一个方形 |
||||||
|
qr_points = [(center_x - length, center_y + length), (center_x + length, center_y + length), (center_x + length, center_y - length), (center_x - length, center_y - length)] |
||||||
|
qr_points = scale_qr_new(foot_points, qr_points, length, (center_x, center_y), scale=1.05) |
||||||
|
return qr_points |
||||||
|
|
||||||
|
def scale_qr_new(foot_points, qr_points, length, center, scale=1.1): |
||||||
|
default_flag = flag = square_in_polygon(qr_points, foot_points) |
||||||
|
center_x, center_y = center[0], center[1] |
||||||
|
if flag: |
||||||
|
while default_flag == flag: |
||||||
|
length *= scale |
||||||
|
# 对每个点进行放大操作并更新坐标 |
||||||
|
qr_points = [((x - center_x) * scale + center_x, (y - center_y) * scale + center_y) for x, y in qr_points] |
||||||
|
flag = square_in_polygon_default(qr_points, foot_points) and square_in_polygon(qr_points, foot_points) |
||||||
|
else: |
||||||
|
while default_flag == flag: |
||||||
|
length /= scale |
||||||
|
# 对每个点进行缩小操作并更新坐标 |
||||||
|
qr_points = [((x - center_x) / scale + center_x, (y - center_y) / scale + center_y) for x, y in qr_points] |
||||||
|
flag = square_in_polygon_default(qr_points, foot_points) and square_in_polygon(qr_points, foot_points) |
||||||
|
return qr_points |
||||||
|
|
||||||
|
# 获取旋转后方形 根据方形原坐标旋转 |
||||||
|
def cal_rota_points(qr_points, center, angle): |
||||||
|
center_x, center_y = center[0], center[1] |
||||||
|
if angle > 0: |
||||||
|
qr_points_after_rotate = [] |
||||||
|
for point in qr_points: |
||||||
|
new_x = (point[0] - center_x) * math.cos(angle) - (point[1] - center_y) * math.sin(angle) + center_x |
||||||
|
new_y = (point[0] - center_x) * math.sin(angle) + (point[1] - center_y) * math.cos(angle) + center_y |
||||||
|
qr_points_after_rotate.append((new_x, new_y)) |
||||||
|
return qr_points_after_rotate |
||||||
|
else: |
||||||
|
return qr_points |
||||||
|
|
||||||
|
# 取中点 |
||||||
|
def cal_middle_point(p1, p2): |
||||||
|
x1, y1 = p1 |
||||||
|
x2, y2 = p2 |
||||||
|
# 中点 |
||||||
|
a1 = (x1 + x2) / 2 |
||||||
|
b1 = (y1 + y2) / 2 |
||||||
|
return a1, b1 |
||||||
|
|
||||||
|
def make_points(qr_points): |
||||||
|
new_points = [] |
||||||
|
index = [0, 1, 2, 3, 0] |
||||||
|
for i in range(4): |
||||||
|
a, b = cal_middle_point(qr_points[index[i]], qr_points[index[i + 1]]) |
||||||
|
new_points.append((a, b)) |
||||||
|
new_points.append((cal_middle_point(qr_points[index[i]], (a, b)))) |
||||||
|
new_points.append((cal_middle_point(qr_points[index[i + 1]], (a, b)))) |
||||||
|
return new_points |
||||||
|
|
||||||
|
#qr_points = get_default_qr_points(foot_points) |
||||||
|
|
||||||
|
min_qr_length = 0.5 |
||||||
|
|
||||||
|
minx = min([p[0] for p in foot_points]) + min_qr_length |
||||||
|
maxx = max([p[0] for p in foot_points]) - min_qr_length |
||||||
|
miny = min([p[1] for p in foot_points]) + min_qr_length |
||||||
|
maxy = max([p[1] for p in foot_points]) - min_qr_length |
||||||
|
|
||||||
|
def rotate_qr_v3(foot_points, qr_points, scale, angle=1): |
||||||
|
best_length = length = cal_square_length(qr_points) |
||||||
|
best_angle, default_angle = 0, 0 |
||||||
|
center_x, center_y = calculate_center(qr_points) |
||||||
|
best_qr_points = qr_points |
||||||
|
# 循环1 求最佳angle 不断增大angle角度 |
||||||
|
while default_angle <= 90: |
||||||
|
qr_points_after_rotate = cal_rota_points(qr_points, (center_x, center_y), default_angle) |
||||||
|
# 在当前angle下增加边长 |
||||||
|
while square_in_polygon(qr_points_after_rotate, foot_points) and dis_flag(qr_points_after_rotate, foot_points): |
||||||
|
flag = True |
||||||
|
best_qr_points = qr_points_after_rotate |
||||||
|
best_angle = default_angle |
||||||
|
best_length = length |
||||||
|
# 对每个点进行放大(或缩小)操作并更新坐标 |
||||||
|
qr_points = [((x - center_x) * scale + center_x, (y - center_y) * scale + center_y) for x, y in qr_points] |
||||||
|
length *= scale |
||||||
|
qr_points_after_rotate = cal_rota_points(qr_points, (center_x, center_y), default_angle) |
||||||
|
# 限制最大边长 |
||||||
|
if best_length > 5: |
||||||
|
return best_qr_points, best_angle, best_length |
||||||
|
|
||||||
|
default_angle += angle |
||||||
|
return best_qr_points, best_angle, best_length |
||||||
|
|
||||||
|
if maxx - minx < maxy - miny: |
||||||
|
step = (maxx - minx) / 15 |
||||||
|
else: |
||||||
|
step = (maxy - miny) / 15 |
||||||
|
|
||||||
|
x, y = minx, miny |
||||||
|
locations = [] |
||||||
|
|
||||||
|
while x <= maxx: |
||||||
|
while y <= maxy: |
||||||
|
locations.append((x, y)) |
||||||
|
y += step |
||||||
|
x += step |
||||||
|
y = miny |
||||||
|
|
||||||
|
# print(f'locations: {locations}') |
||||||
|
locations = [point for point in locations if all(cal_distance(point, f) >= min_qr_length for f in foot_points)] |
||||||
|
location = locations[0] |
||||||
|
qr_points = [(location[0] - 0.5, location[1] - 0.5), (location[0] + 0.5, location[1] - 0.5), (location[0] + 0.5, location[1] + 0.5), (location[0] - 0.5, location[1] + 0.5)] |
||||||
|
plot(foot_points) |
||||||
|
plot(qr_points, 'yellow') |
||||||
|
plt.savefig(f'{workdir}{pid}_{order_id}/fig.png') |
||||||
|
|
||||||
|
best_qr, max_qr_length, best_location, best_rotation = None, 0, None, 0 |
||||||
|
for location in locations: |
||||||
|
plt.plot(location[0], location[1], 'ro') |
||||||
|
qr_points = move_square(qr_points, location) |
||||||
|
if not square_in_polygon(qr_points, foot_points) or not square_in_polygon_default(qr_points, foot_points): |
||||||
|
continue |
||||||
|
else: |
||||||
|
# qr_points = scale_qr(foot_points, qr_points, 1.1) |
||||||
|
# qrs.append(qr_points) |
||||||
|
rotate_qr, rotate_angle, qr_length = rotate_qr_v3(foot_points, qr_points, 1.1, 1) |
||||||
|
if qr_length > max_qr_length: |
||||||
|
max_qr_length = qr_length |
||||||
|
best_location = location |
||||||
|
best_rotation = rotate_angle |
||||||
|
best_qr = rotate_qr |
||||||
|
|
||||||
|
rd = max_qr_length / 1.1 / 2 |
||||||
|
x, y = best_location[0], best_location[1] |
||||||
|
new_qr_points = [(x - rd, y + rd), (x + rd, y + rd), (x + rd, y - rd), (x - rd, y - rd)] |
||||||
|
new_qr_points = cal_rota_points(new_qr_points, best_location, best_rotation) |
||||||
|
return new_qr_points, best_location, max_qr_length / 1.1, best_rotation |
||||||
|
|
||||||
|
def get_plane_points(plane, print_points = False): |
||||||
|
points = [] |
||||||
|
for edge in plane.data.edges: |
||||||
|
point_index = edge.vertices[0] |
||||||
|
point3d = plane.data.vertices[point_index].co |
||||||
|
if print_points: print(point3d) |
||||||
|
points.append((point3d[0], point3d[1])) |
||||||
|
return points |
||||||
|
|
||||||
|
def point_in_polygon(point, polygon): |
||||||
|
num_intersections = 0 |
||||||
|
for i in range(len(polygon)): |
||||||
|
p1, p2 = polygon[i], polygon[(i + 1) % len(polygon)] |
||||||
|
if (p1[1] > point[1]) != (p2[1] > point[1]): |
||||||
|
if point[0] < (p2[0] - p1[0]) * (point[1] - p1[1]) / (p2[1] - p1[1]) + p1[0]: |
||||||
|
num_intersections += 1 |
||||||
|
return num_intersections % 2 == 1 |
||||||
|
|
||||||
|
def square_iou_polygon(square, polygon): |
||||||
|
for point in square: |
||||||
|
if point_in_polygon(point, polygon): |
||||||
|
return True |
||||||
|
return False |
||||||
|
|
||||||
|
def square_in_polygon(square, polygon): |
||||||
|
for point in polygon: |
||||||
|
if point_in_polygon(point, square): |
||||||
|
return False |
||||||
|
return True |
||||||
|
|
||||||
|
def plot(points, color='blue'): |
||||||
|
x = [point[0] for point in points] |
||||||
|
y = [point[1] for point in points] |
||||||
|
if points[-1] != points[0]: |
||||||
|
x.append(points[0][0]) |
||||||
|
y.append(points[0][1]) |
||||||
|
plt.plot(x, y, color=color) |
||||||
|
|
||||||
|
def scale_qr(foot_points, qr_points, scale = 1.1): |
||||||
|
while True: |
||||||
|
old_points = qr_points |
||||||
|
# 计算正方形的中心坐标 |
||||||
|
center_x = sum(x for x, y in qr_points) / len(qr_points) |
||||||
|
center_y = sum(y for x, y in qr_points) / len(qr_points) |
||||||
|
|
||||||
|
# 对每个点进行放大(或缩小)操作并更新坐标 |
||||||
|
qr_points = [((x - center_x) * scale + center_x, (y - center_y) * scale + center_y) for x, y in qr_points] |
||||||
|
|
||||||
|
if not square_in_polygon(qr_points, foot_points): |
||||||
|
qr_points = old_points |
||||||
|
break |
||||||
|
return qr_points |
||||||
|
|
||||||
|
def rotate_qr(foot_points, qr_points, angle = 0.1): |
||||||
|
while True: |
||||||
|
old_points = qr_points |
||||||
|
# 计算正方形的中心坐标 |
||||||
|
center_x = sum(x for x, y in qr_points) / len(qr_points) |
||||||
|
center_y = sum(y for x, y in qr_points) / len(qr_points) |
||||||
|
|
||||||
|
# 对每个点进行放大(或缩小)操作并更新坐标 |
||||||
|
qr_points = [(x - center_x, y - center_y) for x, y in qr_points] |
||||||
|
|
||||||
|
qr_points = [(x * math.cos(angle) - y * math.sin(angle), x * math.sin(angle) + y * math.cos(angle)) for x, y in qr_points] |
||||||
|
|
||||||
|
qr_points = [(x + center_x, y + center_y) for x, y in qr_points] |
||||||
|
|
||||||
|
if not square_in_polygon(qr_points, foot_points): |
||||||
|
qr_points = old_points |
||||||
|
break |
||||||
|
return qr_points |
||||||
|
|
||||||
|
def scale_square(scale, foot_points, back = 0.0): |
||||||
|
while True: |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
old_dimensions = bpy.data.objects['qr'].dimensions.copy() |
||||||
|
active_object(bpy.data.objects['qr']) |
||||||
|
bpy.data.objects['qr'].scale = (scale, scale, 1) |
||||||
|
max_square = get_plane_points(bpy.data.objects['qr']) |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
if not square_in_polygon(max_square, foot_points): |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
bpy.data.objects['qr'].dimensions = (old_dimensions[0] - back, old_dimensions[1] - back, 0) |
||||||
|
max_square = get_plane_points(bpy.data.objects['qr']) |
||||||
|
location, size = get_square_center_size() |
||||||
|
break |
||||||
|
return max_square, location, size |
||||||
|
|
||||||
|
def zoom_square(foot_points, qr_points, center, step_length=0.1): |
||||||
|
while True: |
||||||
|
old_dimensions = bpy.data.objects['qr'].dimensions.copy() |
||||||
|
active_object(bpy.data.objects['qr']) |
||||||
|
# print(f'old_dimensions: {old_dimensions}') |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
bpy.data.objects['qr'].dimensions = (old_dimensions[0] + step_length, old_dimensions[1] + step_length, 0) |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
# print(f'new_dimensions: {bpy.data.objects["qr"].dimensions}') |
||||||
|
max_square = get_plane_points(bpy.data.objects['qr']) |
||||||
|
if not square_in_polygon(max_square, foot_points): |
||||||
|
bpy.data.objects['qr'].dimensions = (old_dimensions[0], old_dimensions[1], 0) |
||||||
|
max_square = get_plane_points(bpy.data.objects['qr']) |
||||||
|
location, size, length = get_square_center_size() |
||||||
|
break |
||||||
|
return max_square, location, size, length |
||||||
|
|
||||||
|
def get_square_center_size(): |
||||||
|
active_object(bpy.data.objects['qr']) |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
location = bpy.data.objects['qr'].location |
||||||
|
size = bpy.data.objects['qr'].dimensions |
||||||
|
length = size[0] |
||||||
|
return location, size, length |
||||||
|
|
||||||
|
def min_x(plane): |
||||||
|
return min([p[0] for p in plane]) |
||||||
|
def min_y(plane): |
||||||
|
return min([p[1] for p in plane]) |
||||||
|
def max_x(plane): |
||||||
|
return max([p[0] for p in plane]) |
||||||
|
def max_y(plane): |
||||||
|
return max([p[1] for p in plane]) |
||||||
|
|
||||||
|
def cal_square_length(square): |
||||||
|
return abs(square[0][0] - square[1][0]) |
||||||
|
|
||||||
|
def cal_square_area(square): |
||||||
|
return abs(square[0][0] - square[1][0]) * abs(square[0][1] - square[3][1]) |
||||||
|
|
||||||
|
def cal_distance(point1, point2): |
||||||
|
return math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2) |
||||||
|
|
||||||
|
def calculate_center(vertices): |
||||||
|
x_sum = sum(x for x, y in vertices) |
||||||
|
y_sum = sum(y for x, y in vertices) |
||||||
|
center_x = x_sum / len(vertices) |
||||||
|
center_y = y_sum / len(vertices) |
||||||
|
return center_x, center_y |
||||||
|
|
||||||
|
def move_square(vertices, new_center): |
||||||
|
center_x, center_y = calculate_center(vertices) |
||||||
|
# print(f'center_x: {center_x}, center_y: {center_y}') |
||||||
|
# print(f'new_center: {new_center}') |
||||||
|
x_diff = center_x - new_center[0] |
||||||
|
y_diff = center_y - new_center[1] |
||||||
|
# print(f'x_diff: {x_diff}, y_diff: {y_diff}') |
||||||
|
# print(f'vertices: {vertices}') |
||||||
|
new_vertices = [(x - x_diff, y - y_diff) for x, y in vertices] |
||||||
|
# print(f'new_vertices: {new_vertices}') |
||||||
|
return new_vertices |
||||||
|
|
||||||
|
def main(workdir, pid, order_id, print_id): |
||||||
|
if not os.path.exists(os.path.join(workdir, f'{pid}_{order_id}')): |
||||||
|
os.makedirs(os.path.join(workdir, f'{pid}_{order_id}')) |
||||||
|
qr_path = os.path.join(workdir, f'{pid}_{order_id}' ,'qr.png') |
||||||
|
gen_data_matrix(print_id, qr_path) |
||||||
|
try: |
||||||
|
get_obj_max_foot() |
||||||
|
except Exception as e: |
||||||
|
print(f"get obj max foot err {e}") |
||||||
|
res = requests.get(f'https://mp.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') |
||||||
|
os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
return |
||||||
|
|
||||||
|
qr_points = get_plane_points(bpy.data.objects['qr']) |
||||||
|
active_object(bpy.data.objects['foot']) |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
foot_points = get_plane_points(bpy.data.objects['foot']) |
||||||
|
# print('foot_points:', foot_points) |
||||||
|
foot_points = nearest_neighbor_sort(foot_points) |
||||||
|
try: |
||||||
|
max_qr, qr_location, max_qr_length, rotation = get_max_qr(foot_points) |
||||||
|
except Exception as e: |
||||||
|
print("异常处理错误") |
||||||
|
res = requests.get(f'https://mp.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') |
||||||
|
os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
return |
||||||
|
print(f'qr_location: {qr_location}') |
||||||
|
plt.plot(qr_location[0], qr_location[1], 'black') |
||||||
|
plot(max_qr, 'green') |
||||||
|
plt.axis('equal') |
||||||
|
plt.savefig(os.path.join(workdir, f'{pid}_{order_id}', 'fig.png')) |
||||||
|
|
||||||
|
bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}_{order_id}/{pid}_qr_start.blend') |
||||||
|
|
||||||
|
qr_position = {} |
||||||
|
qr_location = (qr_location[0], qr_location[1], 0) |
||||||
|
qr_dimensions = (max_qr_length, max_qr_length, 0) |
||||||
|
# print(f'qr_location: {qr_location}') |
||||||
|
# print(f'qr_dimensions: {qr_dimensions}') |
||||||
|
qr_position["location"] = qr_location |
||||||
|
qr_position["dimensions"] = qr_dimensions |
||||||
|
qr_position["rotation"] = rotation |
||||||
|
print(f'qr_position: {qr_position}') |
||||||
|
# with open(os.path.join(workdir, f'{pid}_{order_id}', 'qr_position.txt'), 'w') as f: |
||||||
|
# f.write(json.dumps(qr_position)) |
||||||
|
|
||||||
|
res = requests.get(f'{upload_qr_position_url}?print_id={print_id}&position_data={json.dumps(qr_position)}') |
||||||
|
print(f'update_qr_position_url {upload_qr_position_url}:{res.text}') |
||||||
|
|
||||||
|
bpy.ops.object.load_reference_image(filepath=os.path.join(workdir, f'{pid}_{order_id}', 'qr.png')) |
||||||
|
bpy.context.object.rotation_euler = (math.radians(-180), math.radians(0), rotation) |
||||||
|
bpy.ops.transform.translate(value=qr_location, orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False, snap=False, snap_elements={'INCREMENT'}, use_snap_project=False, snap_target='CLOSEST', use_snap_self=True, use_snap_edit=True, use_snap_nonedit=True, use_snap_selectable=False, release_confirm=True) |
||||||
|
|
||||||
|
bpy.context.object.empty_display_size = qr_dimensions[0] |
||||||
|
|
||||||
|
# for obj in bpy.data.objects: |
||||||
|
# if obj.type == 'MESH' and obj.name != pid: |
||||||
|
# bpy.data.objects.remove(obj) |
||||||
|
|
||||||
|
# qr_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}_{order_id}Tex1_qr.png") |
||||||
|
# jpg_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg") |
||||||
|
# jpg_img = Image.open(jpg_path) |
||||||
|
# shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_noqr.jpg")) |
||||||
|
|
||||||
|
# bpy.context.scene.eyek.res_x = jpg_img.width |
||||||
|
# bpy.context.scene.eyek.res_y = jpg_img.height |
||||||
|
# bpy.context.scene.eyek.path_export_image = qr_path |
||||||
|
# bpy.data.objects[f'{pid}'].select_set(True) |
||||||
|
# bpy.data.objects['Empty'].select_set(True) |
||||||
|
# bpy.context.view_layer.objects.active = bpy.data.objects[f'{pid}'] |
||||||
|
# bpy.ops.eyek.exe() |
||||||
|
|
||||||
|
# qr_img = Image.open(qr_path) |
||||||
|
# jpg_img.paste(qr_img, (0, 0), qr_img) |
||||||
|
# jpg_img.save(jpg_path) |
||||||
|
|
||||||
|
# plt.axis('equal') |
||||||
|
# plt.show() |
||||||
|
|
||||||
|
# 保存blend文件 |
||||||
|
bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}_{order_id}/{pid}_qr_end.blend') |
||||||
|
bpy.ops.wm.quit_blender() |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
get_qr_position_url = 'https://mp.api.suwa3d.com/api/printOrder/getFootCodePositionData' |
||||||
|
upload_qr_position_url = 'https://mp.api.suwa3d.com/api/printOrder/updateFootCodeStatus' |
||||||
|
get_pid_by_printid_url = 'https://mp.api.suwa3d.com/api/printOrder/getPidByPrintId' |
||||||
|
delete_form_foot_code_by_pid = 'https://mp.api.suwa3d.com/api/printOrder/deleteFormFootCodeByPid' |
||||||
|
# get_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/getFootCodePositionData' |
||||||
|
# upload_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/updateFootCodeStatus' |
||||||
|
# get_pid_by_printid_url = 'http://172.31.1.254:8199/api/printOrder/getPidByPrintId' |
||||||
|
|
||||||
|
if platform.system() == 'Windows': |
||||||
|
workdir = 'E:\\print\\foot\\' |
||||||
|
else: |
||||||
|
workdir = '/data/datasets/foot/' |
||||||
|
|
||||||
|
print(sys.argv) |
||||||
|
if len(sys.argv) - (sys.argv.index("--") + 1) < 1: |
||||||
|
print("Usage: blender -b -P auto_dm.py -- <pid_order_id_print_id>") |
||||||
|
sys.exit(1) |
||||||
|
pid, order_id, print_id = sys.argv[sys.argv.index("--") + 1].split('_') |
||||||
|
|
||||||
|
main(workdir, pid, order_id, print_id) |
||||||
@ -0,0 +1,39 @@ |
|||||||
|
|
||||||
|
This version of Blender has been originally released at www.blender.org. |
||||||
|
It is subject to the GNU GPL license, which is part of this download. |
||||||
|
|
||||||
|
Blender, the free and open source 3D creation suite |
||||||
|
Copyright (C) 2023 Blender Foundation |
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU General Public License as published by |
||||||
|
the Free Software Foundation; either version 2 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License |
||||||
|
along with this program; if not, write to the Free Software |
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||||
|
|
||||||
|
The Blender project uses code and libraries which are licensed under terms of |
||||||
|
licenses which are compatible with the GNU GPL 3 or later. |
||||||
|
|
||||||
|
The license of each individual Blender source file can be found in the first |
||||||
|
few lines after "SPDX-License-Identifier:". The license text of every such |
||||||
|
license can be found in license/. |
||||||
|
|
||||||
|
The complete overview of copyright and licenses of third-party libraries used |
||||||
|
by Blender can be found in THIRD-PARTY-LICENSES.txt. |
||||||
|
|
||||||
|
Stichting Blender Foundation |
||||||
|
Amsterdam |
||||||
|
the Netherlands |
||||||
|
Chamber of Commerce: 34176425 |
||||||
|
|
||||||
|
http://www.blender.org |
||||||
|
foundation@blender.org |
||||||
|
|
||||||
@ -0,0 +1,91 @@ |
|||||||
|
import bpy, sys, os, math |
||||||
|
|
||||||
|
pid = '26385' |
||||||
|
workdir = '/home/water/Downloads/' |
||||||
|
|
||||||
|
filename = f'{workdir}{pid}/{pid}_9cm_x1.obj' |
||||||
|
bpy.ops.import_scene.obj(filepath=filename) |
||||||
|
# 坐标复位 |
||||||
|
obj = bpy.context.selected_objects[0] |
||||||
|
obj.rotation_euler[0] = 0 |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') |
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Y', 'Z'}) |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
# bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}_align_yz.obj') |
||||||
|
|
||||||
|
# 躺平到打印机排版需要的坐标与角度 |
||||||
|
obj.rotation_euler = (math.radians(90), math.radians(90), 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
# bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}_rotate_y90.obj') |
||||||
|
|
||||||
|
heights = {} |
||||||
|
min_height = 999999 |
||||||
|
min_i = 0 |
||||||
|
max_height = -999999 |
||||||
|
max_i = 0 |
||||||
|
|
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN') |
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_3', align_axis={'X', 'Y', 'Z'}) |
||||||
|
|
||||||
|
# 步进精度2旋转X轴到180度,找到Y轴最低点和最高点,其中最低点为打印 |
||||||
|
step = 2 |
||||||
|
i = 0 |
||||||
|
while i <= 180: |
||||||
|
obj.rotation_euler = (math.radians(step), 0, 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
if obj.dimensions[1] < min_height: |
||||||
|
min_height = obj.dimensions[1] |
||||||
|
min_i = i |
||||||
|
if obj.dimensions[1] > max_height: |
||||||
|
max_height = obj.dimensions[1] |
||||||
|
max_i = i |
||||||
|
heights[i] = (obj.dimensions[0], obj.dimensions[1], obj.dimensions[2]) |
||||||
|
print(i, heights[i]) |
||||||
|
i += step |
||||||
|
|
||||||
|
obj.rotation_euler = (0, 0, 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
obj.rotation_euler = (math.radians(min_i), 0, 0) |
||||||
|
bpy.ops.object.transform_apply(rotation=True) |
||||||
|
#bpy.ops.export_scene.obj(filepath=f'{workdir}{pid}_miny.obj') |
||||||
|
print(f'最小高度: {min_height} @ {heights[min_i]}min_i:{min_i}' , f'最大高度: {max_height} @ {heights[max_i]}max_i:{max_i}') |
||||||
|
|
||||||
|
offset = 45.5 |
||||||
|
radian = math.radians(90) |
||||||
|
bpy.ops.mesh.primitive_plane_add(size=200, enter_editmode=False, align='WORLD', location=(offset, 0, 0), rotation=(0, radian, 0), scale=(1, 1, 1)) |
||||||
|
|
||||||
|
# 布尔切割,保留交集切面 |
||||||
|
bpy.ops.object.modifier_add(type='BOOLEAN') |
||||||
|
bpy.context.object.modifiers["Boolean"].object = bpy.data.objects[pid] |
||||||
|
bpy.context.object.modifiers["Boolean"].operation = 'INTERSECT' |
||||||
|
bpy.context.object.modifiers["Boolean"].solver = 'FAST' |
||||||
|
bpy.ops.object.modifier_apply(modifier="Boolean") |
||||||
|
|
||||||
|
# 拆分切割面为多个多边形,然后遍历多边形,找到最大的面积 |
||||||
|
bpy.ops.mesh.separate(type='LOOSE') |
||||||
|
|
||||||
|
max_area = 0 |
||||||
|
max_obj = None |
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.type == 'MESH' and obj.name.startswith('Plane'): |
||||||
|
area = obj.data.polygons[0].area |
||||||
|
if area > max_area: |
||||||
|
max_area = area |
||||||
|
max_obj = obj |
||||||
|
|
||||||
|
# 选中最大面积的多边形,然后计算中心点 |
||||||
|
bpy.ops.object.select_all(action='DESELECT') |
||||||
|
max_obj.select_set(True) |
||||||
|
bpy.context.view_layer.objects.active = max_obj |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') |
||||||
|
|
||||||
|
bpy.ops.import_scene.obj(filepath=f'{workdir}{pid}/qrcode.obj') |
||||||
|
qr_obj = bpy.data.objects['qrcode'] |
||||||
|
shore_obj = bpy.data.objects['Cube.001'] |
||||||
|
qr_obj.location = (max_obj.location[0] - qr_obj.dimensions[0] / 2 - shore_obj.dimensions[0], max_obj.location[1], max_obj.location[2]) |
||||||
|
shore_obj.location = (qr_obj.location[0]-0.01, max_obj.location[1], max_obj.location[2]) |
||||||
|
|
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.type == 'MESH' and obj.name.startswith('Plane'): |
||||||
|
bpy.data.objects.remove(obj) |
||||||
@ -0,0 +1 @@ |
|||||||
|
{"data": [{"location": {"x": 7.40227746963501, "y": -0.10000000149011612, "z": 4.669338226318359}, "rotation_euler": {"x": 1.570796251296997, "y": 3.0, "z": -0.0}, "scale": {"x": 4.177248001098633, "y": 4.177248001098633, "z": 4.177248001098633}, "fov_x": -4.177248001098633, "limit_near": 0.009999999776482582, "limit_far": 200.0, "image_path": "E:\\print\\foot\\152696_56145\\qr.png"}]} |
||||||
@ -0,0 +1,715 @@ |
|||||||
|
import os, sys, bpy, math, time, platform, cairosvg, ppf.datamatrix, shutil, requests, json, redis, oss2, cv2,qrcode |
||||||
|
from retrying import retry |
||||||
|
import subprocess |
||||||
|
import random |
||||||
|
import numpy as np |
||||||
|
import matplotlib.pyplot as plt |
||||||
|
from PIL import Image, ImageEnhance |
||||||
|
from addon_utils import enable |
||||||
|
import logging,atexit,platform |
||||||
|
# if platform.system() == 'Windows': |
||||||
|
sys.path.append('/home/acprint/code/libs/') |
||||||
|
import common |
||||||
|
logging.basicConfig(filename='foot_update_res.log', level=logging.ERROR) |
||||||
|
enable('io_import_images_as_planes') |
||||||
|
enable('eyek_addon') |
||||||
|
|
||||||
|
|
||||||
|
#查询影棚ID |
||||||
|
def getPSid(pid): |
||||||
|
res = requests.get("https://mp.api.suwa3d.com/api/customerP3dLog/photoStudio",params={"pid":pid}) |
||||||
|
res = json.loads(res.text) |
||||||
|
return str(res['data']) |
||||||
|
|
||||||
|
|
||||||
|
def restart_current_process(new_command): |
||||||
|
try: |
||||||
|
# 保存新进程的命令 |
||||||
|
command = new_command.split() |
||||||
|
|
||||||
|
# 启动新进程 |
||||||
|
new_process = subprocess.Popen(command) |
||||||
|
|
||||||
|
# 打印新进程的PID |
||||||
|
print(f"New process started with PID: {new_process.pid}") |
||||||
|
|
||||||
|
# 终止当前进程 |
||||||
|
os._exit(0) |
||||||
|
|
||||||
|
except Exception as e: |
||||||
|
print(f"An error occurred: {e}") |
||||||
|
|
||||||
|
#生成二维码图片 |
||||||
|
def gen_data_matrix(print_id,pid, qr_path, size = 300): |
||||||
|
psid = getPSid(pid) |
||||||
|
if int(psid) == 0: |
||||||
|
print("费工夫构建脚底二维码") |
||||||
|
# if use_foot_type == "short_url": |
||||||
|
#调用接口获取短网址信息 |
||||||
|
short_url = get_short_url(print_id) |
||||||
|
if short_url == False: |
||||||
|
return |
||||||
|
temp_foot_data = short_url |
||||||
|
|
||||||
|
#生成二维码 |
||||||
|
qr = qrcode.QRCode( |
||||||
|
version=1, |
||||||
|
error_correction = qrcode.constants.ERROR_CORRECT_L, |
||||||
|
box_size=10, |
||||||
|
border=2, |
||||||
|
) |
||||||
|
qr.add_data(temp_foot_data) |
||||||
|
qr.make(fit=True) |
||||||
|
img = qr.make_image(fill_color="black",back_color="white").resize((size,size)) |
||||||
|
img.save(qr_path) |
||||||
|
else: |
||||||
|
#正常生成不是常规的二维码 |
||||||
|
print("排除费工夫正常构建二维码") |
||||||
|
svg = ppf.datamatrix.DataMatrix(f'p{print_id}').svg() |
||||||
|
cairosvg.svg2png(bytestring=svg, write_to=qr_path, output_width=size, output_height=size, background_color='white') |
||||||
|
|
||||||
|
def active_object(obj): |
||||||
|
bpy.context.view_layer.objects.active = obj |
||||||
|
obj.select_set(True) |
||||||
|
#下载oss的文件 |
||||||
|
@retry(stop_max_attempt_number=10, wait_fixed=2000) |
||||||
|
def down_obj_fromoss(pid, print_type=1, order_id=None): |
||||||
|
# print_type:// 打印状态 1:正常打印 2:重打 3:加打,4: 样品 |
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 开始下载模型,若网络异常将每间隔2秒重试10次...') |
||||||
|
if not order_id is None: |
||||||
|
path = os.path.join(workdir, f'{pid}_{order_id}') |
||||||
|
else: |
||||||
|
path = os.path.join(workdir, pid) |
||||||
|
if os.path.exists(path): |
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 已存在模型文件,删除后重新下载') |
||||||
|
shutil.rmtree(path, ignore_errors=True) |
||||||
|
os.makedirs(path) |
||||||
|
|
||||||
|
# 下载分2种情况,一种是第一次打印,下载标准{pid}.obj,{pid}.mtl,{pid}Tex1.jpg,另一种是重打或加打,obj文件名可以从oss上任意获取一个,但是mtl和jpg文件名是固定的 |
||||||
|
res = oss_client.get_object_to_file(f'objs/print/{pid}/{pid}.mtl', os.path.join(path, f'{pid}.mtl')) |
||||||
|
last_modified = oss_client.get_object_meta(f"objs/print/{pid}/{pid}.mtl").last_modified |
||||||
|
print(f'mtl文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') |
||||||
|
print(f'下载文件:objs/print/{pid}/{pid}.mtl,状态:{res.status}') |
||||||
|
if oss_client.object_exists(f'objs/print/{pid}/{pid}Tex1.jpg'): |
||||||
|
res = oss_client.get_object_to_file(f'objs/print/{pid}/{pid}Tex1.jpg', os.path.join(path, f'{pid}Tex1.jpg')) |
||||||
|
last_modified = oss_client.get_object_meta(f"objs/print/{pid}/{pid}Tex1.jpg").last_modified |
||||||
|
print(f'jpg文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') |
||||||
|
print(f'下载文件:objs/print/{pid}/{pid}Tex1.jpg,状态:{res.status}') |
||||||
|
else: |
||||||
|
res = oss_client.get_object_to_file(f'objs/print/{pid}/{pid}.jpg', os.path.join(path, f'{pid}Tex1.jpg')) |
||||||
|
last_modified = oss_client.get_object_meta(f"objs/print/{pid}/{pid}.jpg").last_modified |
||||||
|
print(f'jpg文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') |
||||||
|
print(f'下载文件:objs/print/{pid}/{pid}.jpg,状态:{res.status}') |
||||||
|
if oss_client.object_exists(f'objs/print/{pid}/{pid}.obj'): |
||||||
|
res = oss_client.get_object_to_file(f'objs/print/{pid}/{pid}.obj', os.path.join(path, f'{pid}.obj')) |
||||||
|
last_modified = oss_client.get_object_meta(f"objs/print/{pid}/{pid}.obj").last_modified |
||||||
|
print(f'obj文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') |
||||||
|
print(f'下载文件:objs/print/{pid}/{pid}.obj,状态:{res.status}') |
||||||
|
else: |
||||||
|
prefix = f'objs/print/{pid}/' |
||||||
|
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix) |
||||||
|
for file in filelist: |
||||||
|
filename = file.key.split('/')[-1] |
||||||
|
if filename == '': continue |
||||||
|
if filename.endswith(f'.obj'): |
||||||
|
res = oss_client.get_object_to_file(file.key, os.path.join(path, f'{pid}.obj')) |
||||||
|
last_modified = oss_client.get_object_meta(file.key).last_modified |
||||||
|
print(f'obj文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}') |
||||||
|
print(f'下载文件:{file.key},状态:{res.status}') |
||||||
|
break |
||||||
|
#查找obj |
||||||
|
def find_obj(pid, order_id): |
||||||
|
find = False |
||||||
|
if os.path.exists(os.path.join(sourceFilePath, f'{pid}_{order_id}', f'{pid}.obj')): |
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.obj')): |
||||||
|
#复制文件到 指定目录 |
||||||
|
shutil.copytree(os.path.join(sourceFilePath, f'{pid}_{order_id}'), os.path.join(workdir,f'{pid}_{order_id}')) |
||||||
|
|
||||||
|
# shutil.copy(f"/data/datasets/print/{pid}_{order_id}/{pid}.obj", os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.obj')) |
||||||
|
# shutil.copy(f"/data/datasets/print/{pid}_{order_id}/{pid}.mtl", os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.mtl')) |
||||||
|
# shutil.copy(f"/data/datasets/print/{pid}_{order_id}/{pid}.Tex1.jpg", os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg')) |
||||||
|
else: |
||||||
|
print('没有找到obj模型文件,开始下载') |
||||||
|
down_obj_fromoss(pid, order_id=order_id) |
||||||
|
if os.path.exists(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg')): |
||||||
|
shutil.move(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}.jpg'), os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg')) |
||||||
|
with open(os.path.join(workdir, f'{pid}_{order_id}', 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, f'{pid}_{order_id}', f'{pid}.mtl'), 'w') as f: |
||||||
|
f.writelines(lines) |
||||||
|
filelist = os.listdir(os.path.join(workdir, f'{pid}_{order_id}')) |
||||||
|
for filename in filelist: |
||||||
|
if f'{pid}.obj' in filename: |
||||||
|
find = True |
||||||
|
return filename |
||||||
|
print('没有找到obj模型文件') |
||||||
|
return '' |
||||||
|
|
||||||
|
def find_pid_objname(pid): |
||||||
|
for obj in bpy.data.objects: |
||||||
|
if obj.name.startswith(str(pid)): |
||||||
|
return obj.name |
||||||
|
|
||||||
|
def ps_color_scale_adjustment(image, shadow=0, highlight=255, midtones=1): |
||||||
|
''' |
||||||
|
模拟 PS 的色阶调整; 0 <= Shadow < Highlight <= 255 |
||||||
|
:param image: 传入的图片 |
||||||
|
:param shadow: 黑场(0-Highlight) |
||||||
|
:param highlight: 白场(Shadow-255) |
||||||
|
:param midtones: 灰场(9.99-0.01) |
||||||
|
:return: 图片 |
||||||
|
''' |
||||||
|
if highlight > 255: |
||||||
|
highlight = 255 |
||||||
|
if shadow < 0: |
||||||
|
shadow = 0 |
||||||
|
if shadow >= highlight: |
||||||
|
shadow = highlight - 2 |
||||||
|
if midtones > 9.99: |
||||||
|
midtones = 9.99 |
||||||
|
if midtones < 0.01: |
||||||
|
midtones = 0.01 |
||||||
|
image = np.array(image, dtype=np.float16) |
||||||
|
# 计算白场 黑场离差 |
||||||
|
Diff = highlight - shadow |
||||||
|
image = image - shadow |
||||||
|
image[image < 0] = 0 |
||||||
|
image = (image / Diff) ** (1 / midtones) * 255 |
||||||
|
image[image > 255] = 255 |
||||||
|
image = np.array(image, dtype=np.uint8) |
||||||
|
|
||||||
|
return image |
||||||
|
|
||||||
|
|
||||||
|
def show_histogram(image, image_id, save_hist_dir, min_threshold, max_threshold): |
||||||
|
''' |
||||||
|
画出直方图展示 |
||||||
|
:param image: 导入图片 |
||||||
|
:param image_id: 图片id编号 |
||||||
|
:param save_hist_dir: 保存路径 |
||||||
|
:param min_threshold: 最小阈值 |
||||||
|
:param max_threshold: 最大阈值 |
||||||
|
:return: 原图image,和裁剪原图直方图高低阈值后的图片image_change |
||||||
|
''' |
||||||
|
plt.rcParams['font.family'] = 'SimHei' |
||||||
|
plt.rcParams['axes.unicode_minus'] = False |
||||||
|
plt.hist(image.ravel(), 254, range=(2, 256), density=False) |
||||||
|
plt.hist(image.ravel(), 96, range=(2, 50), density=False) # 放大 range(0, 50),bins值最好是range的两倍,显得更稀疏,便于对比 |
||||||
|
plt.hist(image.ravel(), 110, range=(200, 255), density=False) # 放大 range(225, 255) |
||||||
|
plt.annotate('thresh1=' + str(min_threshold), # 文本内容 |
||||||
|
xy=(min_threshold, 0), # 箭头指向位置 # 阈值设定值! |
||||||
|
xytext=(min_threshold, 500000), # 文本位置 # 阈值设定值! |
||||||
|
arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头 |
||||||
|
plt.annotate('thresh2=' + str(max_threshold), # 文本内容 |
||||||
|
xy=(max_threshold, 0), # 箭头指向位置 # 阈值设定值! |
||||||
|
xytext=(max_threshold, 500000), # 文本位置 # 阈值设定值! |
||||||
|
arrowprops=dict(facecolor='black', width=1, shrink=5, headwidth=2)) # 箭头 |
||||||
|
# 在y轴上绘制一条直线 |
||||||
|
# plt.axhline(y=10000, color='r', linestyle='--', linewidth=0.5) |
||||||
|
plt.title(str(image_id)) |
||||||
|
# plt.show() |
||||||
|
# 保存直方图 |
||||||
|
save_hist_name = os.path.join(save_hist_dir, f'{image_id}_{min_threshold}&{max_threshold}.jpg') |
||||||
|
plt.savefig(save_hist_name) |
||||||
|
# 清空画布, 防止重叠展示 |
||||||
|
plt.clf() |
||||||
|
|
||||||
|
|
||||||
|
def low_find_histogram_range(image, target_frequency): |
||||||
|
''' |
||||||
|
循环查找在 target_frequency (y)频次限制下的直方图区间值(x) |
||||||
|
:param image: 导入图片 |
||||||
|
:param target_frequency: 直方图 y 频次限制条件 |
||||||
|
:return: 直方图区间 x,和 该区间频次 y |
||||||
|
''' |
||||||
|
# 计算灰度直方图 |
||||||
|
hist, bins = np.histogram(image, bins=256, range=[0, 256]) |
||||||
|
# 初始化区间和频次 |
||||||
|
interval = 2 |
||||||
|
frequency = hist[255] |
||||||
|
while frequency < target_frequency: |
||||||
|
# 更新区间和频次 |
||||||
|
interval += 1 |
||||||
|
# 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。 |
||||||
|
frequency = hist[interval] if hist[interval] is not None else 0 |
||||||
|
frequency += hist[interval] if hist[interval] is not None else 0 |
||||||
|
# 如果频次接近10000则停止循环 |
||||||
|
if target_frequency - 2000 <= frequency <= target_frequency + 1000: |
||||||
|
break |
||||||
|
|
||||||
|
return interval, frequency |
||||||
|
|
||||||
|
|
||||||
|
def high_find_histogram_range(image, target_frequency): |
||||||
|
''' |
||||||
|
循环查找在 target_frequency (y)频次限制下的直方图区间值(x) |
||||||
|
:param image: 导入图片 |
||||||
|
:param target_frequency: 直方图 y 频次限制条件 |
||||||
|
:return: 直方图区间 x,和 该区间频次 y |
||||||
|
''' |
||||||
|
# 计算灰度直方图 |
||||||
|
hist, bins = np.histogram(image, bins=256, range=[0, 256]) |
||||||
|
# 初始化区间和频次 |
||||||
|
interval = 255 |
||||||
|
frequency = hist[255] |
||||||
|
while frequency < target_frequency: |
||||||
|
# 更新区间和频次 |
||||||
|
interval -= 1 |
||||||
|
# 检查直方图的频次是否为None,如果频次是None,则将其设为0,这样可以避免将None和int进行比较报错。 |
||||||
|
frequency = hist[interval] if hist[interval] is not None else 0 |
||||||
|
frequency += hist[interval] if hist[interval] is not None else 0 |
||||||
|
# 如果频次接近10000则停止循环 |
||||||
|
if target_frequency - 2000 <= frequency <= target_frequency + 2000: |
||||||
|
break |
||||||
|
|
||||||
|
return interval, frequency |
||||||
|
|
||||||
|
def reduce_sharpness(image, factor): |
||||||
|
''' |
||||||
|
使用PIL库减弱图像锐度 |
||||||
|
:param image: 图像 |
||||||
|
:param factor: 锐度因子,0表示最大程度减弱锐度,1表示原始图像 |
||||||
|
:return: 减弱锐度后的图像 |
||||||
|
''' |
||||||
|
# OpenCV 格式的图像转换为 PIL 的 Image 对象 |
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) |
||||||
|
pil_image = Image.fromarray(image_rgb) |
||||||
|
enhancer = ImageEnhance.Sharpness(pil_image) |
||||||
|
reduced_image = enhancer.enhance(factor) |
||||||
|
# PIL 的 Image 对象转换为 OpenCV 的图像格式 |
||||||
|
image_array = np.array(reduced_image) |
||||||
|
sharpened_image = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR) |
||||||
|
|
||||||
|
return sharpened_image |
||||||
|
|
||||||
|
def sharpening_filter(image): |
||||||
|
''' |
||||||
|
锐化滤波器对图片进行锐化,增强图像中的边缘和细节 |
||||||
|
:param image: 导入图片 |
||||||
|
:return: 锐化后的图片 |
||||||
|
''' |
||||||
|
sharp_kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]]) |
||||||
|
sharpened_image = cv2.filter2D(image, -1, sharp_kernel) |
||||||
|
return sharpened_image |
||||||
|
|
||||||
|
def find_last_x(image, slope_threshold = 1000): |
||||||
|
x = [] |
||||||
|
y = [] |
||||||
|
hist, bins = np.histogram(image, bins=256, range=[0, 256]) |
||||||
|
|
||||||
|
#找到50以内的最高峰 |
||||||
|
max_y = 0 |
||||||
|
max_i = 5 |
||||||
|
for i in range(5, 50): |
||||||
|
if hist[i] > max_y: |
||||||
|
max_y = hist[i] |
||||||
|
max_i = i |
||||||
|
print(f'50以内最高峰值y:{max_y},最高峰位置x:{max_i}') |
||||||
|
|
||||||
|
for i in range(2, max_i): |
||||||
|
x.append(i) |
||||||
|
y.append(hist[i]) |
||||||
|
slopes = [abs(y[i + 1] - y[i]) for i in range(len(x) - 1)] |
||||||
|
|
||||||
|
current_interval = [] |
||||||
|
max_interval = [] |
||||||
|
max_x = {} |
||||||
|
for i, slope in enumerate(slopes): |
||||||
|
current_interval.append(slope) |
||||||
|
if slope >= slope_threshold: |
||||||
|
if len(current_interval) > len(max_interval): |
||||||
|
max_interval = current_interval.copy() |
||||||
|
max_x[x[i]] = slope |
||||||
|
current_interval = [] |
||||||
|
|
||||||
|
print(max_x) |
||||||
|
last_x = list(max_x)[-1] |
||||||
|
last_y = max_x[last_x] |
||||||
|
return last_x, last_y |
||||||
|
|
||||||
|
def find_last_high(image, slope_threshold = 2500): |
||||||
|
x = [] |
||||||
|
y = [] |
||||||
|
hist, bins = np.histogram(image, bins=255, range=[2, 255]) |
||||||
|
|
||||||
|
#找到200以上的最高峰 |
||||||
|
max_y = 0 |
||||||
|
max_i = 254 |
||||||
|
for i in range(220, 255): |
||||||
|
if hist[i] > max_y: |
||||||
|
max_y = hist[i] |
||||||
|
max_i = i |
||||||
|
print(f'200以上的最高峰值y:{max_y},最高峰位置x:{max_i}') |
||||||
|
|
||||||
|
for i in range(max_i, 255): |
||||||
|
x.append(i) |
||||||
|
y.append(hist[i]) |
||||||
|
slopes = [abs(y[i + 1] - y[i]) for i in range(len(x) - 1)] |
||||||
|
|
||||||
|
current_interval = [] |
||||||
|
max_interval = [] |
||||||
|
max_x = {} |
||||||
|
find = False |
||||||
|
for i in range(len(slopes) - 1, -1, -1): |
||||||
|
slope = slopes[i] |
||||||
|
current_interval.append(slope) |
||||||
|
if slope >= slope_threshold: |
||||||
|
find = True |
||||||
|
if len(current_interval) > len(max_interval): |
||||||
|
max_interval = current_interval.copy() |
||||||
|
max_x[x[i]] = slope |
||||||
|
current_interval = [] |
||||||
|
#如果没有找到200以上很平,而且高度小于5000,就按220位置削平 |
||||||
|
if not find and hist[220] < 5000: |
||||||
|
max_x[220] = hist[220] |
||||||
|
|
||||||
|
print(max_x) |
||||||
|
if len(max_x) > 0: |
||||||
|
last_x = list(max_x)[0] |
||||||
|
last_y = max_x[last_x] |
||||||
|
else: |
||||||
|
print(f'找不到200以上曲线较平的区间,使用254作为最高峰') |
||||||
|
last_x = 254 |
||||||
|
last_y = hist[254] |
||||||
|
return last_x, last_y |
||||||
|
|
||||||
|
def remove_gray_and_sharpening(jpg_path): |
||||||
|
input_image = cv2.imread(jpg_path) |
||||||
|
# low_x_thresh, low_y_frequency = low_find_histogram_range(input_image, low_y_limit) |
||||||
|
low_x_thresh, low_y_frequency = find_last_x(input_image) |
||||||
|
# high_x_thresh, high_y_frequency = high_find_histogram_range(input_image, high_y_limit) |
||||||
|
high_x_thresh, high_y_frequency = find_last_high(input_image) |
||||||
|
print(f"{low_x_thresh} 区间, {low_y_frequency} 频次") |
||||||
|
print(f"{high_x_thresh} 区间, {high_y_frequency} 频次") |
||||||
|
high_output_image = ps_color_scale_adjustment(input_image, shadow=low_x_thresh, highlight=high_x_thresh, midtones=1) |
||||||
|
# high_output_image = ps_color_scale_adjustment(low_ouput_image, shadow=0, highlight=high_x_thresh, midtones=1) |
||||||
|
|
||||||
|
# # 人体贴图和黑色背景交界处不进行锐化 |
||||||
|
# gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY) |
||||||
|
# _, thresh = cv2.threshold(gray, 2, 255, cv2.THRESH_BINARY) |
||||||
|
# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7)) |
||||||
|
# gradient = cv2.morphologyEx(thresh, cv2.MORPH_GRADIENT, kernel) |
||||||
|
# roi_gradient = cv2.bitwise_and(high_output_image, high_output_image, mask=gradient) |
||||||
|
|
||||||
|
# # 锐化滤波器 |
||||||
|
# # sharpened_image = sharpening_filter(high_output_image) |
||||||
|
# sharpened_image = reduce_sharpness(high_output_image, factor=4) |
||||||
|
# # 将原图边界替换锐化后的图片边界 |
||||||
|
# sharpened_image[gradient != 0] = roi_gradient[gradient != 0] |
||||||
|
|
||||||
|
# 直方图标记并保存 |
||||||
|
# show_histogram(input_image, img_id, low_x_thresh, high_x_thresh) |
||||||
|
cv2.imwrite(jpg_path, high_output_image, [cv2.IMWRITE_JPEG_QUALITY, 95]) # 保存图片的质量是原图的 95% |
||||||
|
|
||||||
|
def main(workdir, r, print_id): |
||||||
|
print('脚底板二维码程序开始运行...') |
||||||
|
only_one = False |
||||||
|
while True: |
||||||
|
#随机休眠 1- 9 |
||||||
|
time.sleep(random.uniform(1, 9)) |
||||||
|
if print_id == '0': |
||||||
|
try: |
||||||
|
if r.llen('model:foot') == 0: |
||||||
|
# print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '队列为空,5秒后重试') |
||||||
|
time.sleep(5) |
||||||
|
continue |
||||||
|
except Exception as e: |
||||||
|
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), 'redis连接异常,5秒后重试') |
||||||
|
print(e) |
||||||
|
time.sleep(5) |
||||||
|
r = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6) |
||||||
|
# r = redis.Redis(host='172.31.1.254', password='', port=6379, db=6) |
||||||
|
# continue |
||||||
|
# 打印队列里面的全部内容 |
||||||
|
print(f'当前model:foot队列长度:{r.llen("model:foot")}') |
||||||
|
for i in r.lrange('model:foot', 0, -1): |
||||||
|
print(i) |
||||||
|
print_id = r.lpop('model:foot') |
||||||
|
if print_id is None: |
||||||
|
print_id = '0' |
||||||
|
continue |
||||||
|
#判断是否存在相同的值 |
||||||
|
isHaveAlready = 0 |
||||||
|
for i in r.lrange('model:foot', 0, -1): |
||||||
|
if i == print_id: |
||||||
|
isHaveAlready = 1 |
||||||
|
|
||||||
|
if isHaveAlready == 1: |
||||||
|
print_id = '0' |
||||||
|
continue |
||||||
|
|
||||||
|
print_id = print_id.decode('utf-8') |
||||||
|
else: |
||||||
|
print(f'接收到运行一个{print_id}任务') |
||||||
|
only_one = True |
||||||
|
|
||||||
|
res = requests.get(f'{get_pid_by_printid_url}?print_id={print_id}') |
||||||
|
print('获取pid:', f'{get_pid_by_printid_url}?print_id={print_id}', res.text) |
||||||
|
resCode = json.loads(res.text)['code'] |
||||||
|
#该笔订单的获取信息有误,可能信息还没有成功 |
||||||
|
if int(resCode) != 1000: |
||||||
|
tempMesg = json.loads(res.text)['message'] |
||||||
|
#判断是否包含 不存在 |
||||||
|
if tempMesg == "打印订单不存在": |
||||||
|
print(f"打印ID{print_id}打印订单不存在,跳过") |
||||||
|
# res = requests.get(f'https://mp.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') |
||||||
|
os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
return |
||||||
|
|
||||||
|
print("获取打印任务信息有问题,重新任务队列,打印id-",print_id,"重新执行脚底板任务,等待20s") |
||||||
|
time.sleep(20) |
||||||
|
#将pid 重新扔进队列 |
||||||
|
r.lpush("model:foot", print_id) |
||||||
|
#重新调用脚底板程序 |
||||||
|
#os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
continue |
||||||
|
use_foot_type = json.loads(res.text)['data']['use_foot_type'] |
||||||
|
pid = json.loads(res.text)['data']['pid'] |
||||||
|
order_id = json.loads(res.text)['data']['order_id'] |
||||||
|
|
||||||
|
filename = os.path.join(workdir, f'{pid}_{order_id}', find_obj(pid, order_id)) |
||||||
|
print('导入obj文件:', filename) |
||||||
|
# return |
||||||
|
if only_one: |
||||||
|
print(f'接收到运行一个{print_id}任务,强制调用cal_foot_position.py计算并上传qr_position') |
||||||
|
os.system(f'blender -b -P cal_foot_position.py -- {pid}_{order_id}_{print_id}') |
||||||
|
print("延时20s,等待计算脚底板坐标") |
||||||
|
time.sleep(20) |
||||||
|
res = requests.get(f'{get_qr_position_url}?print_id={print_id}') |
||||||
|
print('从云端获取的qr_position1:', res.text) |
||||||
|
codeTemp = json.loads(res.text)['code'] |
||||||
|
if str(codeTemp) == "-1": |
||||||
|
#移除该脚底板的面积处理 |
||||||
|
res = requests.get(f'https://mp.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') |
||||||
|
os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
return |
||||||
|
qr_position = json.loads(res.text)['data']['position_data'] |
||||||
|
else: |
||||||
|
#从云端获取qr_position,如果获取为空,调用cal_foot_position.py计算并上传qr_position,再重新读取qr_position.txt |
||||||
|
res = requests.get(f'{get_qr_position_url}?print_id={print_id}') |
||||||
|
print('从云端获取的qr_position2:', res.text) |
||||||
|
|
||||||
|
codeTemp = json.loads(res.text)['code'] |
||||||
|
if str(codeTemp) == "-1": |
||||||
|
continue |
||||||
|
|
||||||
|
qr_position = json.loads(res.text)['data']['position_data'] |
||||||
|
print("云端获取的坐标数据",qr_position) |
||||||
|
if qr_position == '': |
||||||
|
time.sleep(3) |
||||||
|
print('qr_position为空,调用cal_foot_position.py计算并上传qr_position') |
||||||
|
os.system(f'blender -b -P cal_foot_position.py -- {pid}_{order_id}_{print_id}') |
||||||
|
print("延时20s,等待计算脚底板坐标") |
||||||
|
time.sleep(20) |
||||||
|
res = requests.get(f'{get_qr_position_url}?print_id={print_id}') |
||||||
|
print('从云端获取的qr_position3:', res.text) |
||||||
|
qr_position = json.loads(res.text)['data']['position_data'] |
||||||
|
else: |
||||||
|
qr_position = json.loads(qr_position) |
||||||
|
|
||||||
|
if qr_position == "": |
||||||
|
print("获取脚底坐标数据为空,重新任务队列,打印id-",print_id,"重新执行脚底板任务") |
||||||
|
#将pid 重新扔进队列 |
||||||
|
r.lpush("model:foot", print_id) |
||||||
|
#重新调用脚底板程序 |
||||||
|
#os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
continue |
||||||
|
|
||||||
|
if type(qr_position) == str: qr_position = json.loads(qr_position) |
||||||
|
print(f'type of qr_position:{type(qr_position)}') |
||||||
|
print(f'qr_position:{qr_position}') |
||||||
|
|
||||||
|
qr_position['location'][2] = -0.1 |
||||||
|
|
||||||
|
temp_foot_data = print_id |
||||||
|
# 根据print_id生成qr码 |
||||||
|
qr_path = os.path.join(workdir, f'{pid}_{order_id}' ,'qr.png') |
||||||
|
# if use_foot_type == "short_url": |
||||||
|
# #调用接口获取短网址信息 |
||||||
|
# short_url = get_short_url(print_id) |
||||||
|
# if short_url == False: |
||||||
|
# return |
||||||
|
# temp_foot_data = short_url |
||||||
|
# print(f'temp_foot_data---{temp_foot_data}') |
||||||
|
gen_data_matrix(print_id,pid, qr_path) |
||||||
|
|
||||||
|
|
||||||
|
# 导入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=filename) |
||||||
|
#bpy.ops.wm.obj_import(filepath=filename) |
||||||
|
obj = bpy.context.selected_objects[0] |
||||||
|
bpy.context.view_layer.objects.active = obj |
||||||
|
obj.select_set(True) |
||||||
|
|
||||||
|
pid_objname = find_pid_objname(pid) |
||||||
|
|
||||||
|
scale = 90 / obj.dimensions.y |
||||||
|
obj.scale = (scale, scale, scale) |
||||||
|
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'}) |
||||||
|
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_MASS', center='MEDIAN') |
||||||
|
obj.location[0] = 0 |
||||||
|
obj.location[1] = 0 |
||||||
|
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) |
||||||
|
|
||||||
|
print(f'qr_position:{qr_position}') |
||||||
|
print(f'qr_position_type:{type(qr_position)}') |
||||||
|
|
||||||
|
# 根据qr_position的值,恢复qr的位置和尺寸,重新生成贴图 |
||||||
|
bpy.ops.object.load_reference_image(filepath=os.path.join(workdir, f'{pid}_{order_id}', 'qr.png')) |
||||||
|
bpy.context.object.rotation_euler = (math.radians(-180), math.radians(0), qr_position['rotation']) |
||||||
|
bpy.ops.transform.translate(value=qr_position['location'], orient_type='GLOBAL', orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)), orient_matrix_type='GLOBAL', mirror=False, use_proportional_edit=False, proportional_edit_falloff='SMOOTH', proportional_size=1, use_proportional_connected=False, use_proportional_projected=False, snap=False, snap_elements={'INCREMENT'}, use_snap_project=False, snap_target='CLOSEST', use_snap_self=True, use_snap_edit=True, use_snap_nonedit=True, use_snap_selectable=False, release_confirm=True) |
||||||
|
|
||||||
|
bpy.context.object.empty_display_size = qr_position['dimensions'][0] |
||||||
|
|
||||||
|
qr_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}_{order_id}Tex1_qr.png") |
||||||
|
jpg_path = os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg") |
||||||
|
#判断是否存在 jpg_path,不存在就执行下一个任务 |
||||||
|
if not os.path.exists(jpg_path): |
||||||
|
#移除该脚底板的面积处理 |
||||||
|
res = requests.get(f'https://mp.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}') |
||||||
|
os.system(f'blender -b -P fill_dm_code.py') |
||||||
|
return |
||||||
|
|
||||||
|
jpg_img = Image.open(jpg_path) |
||||||
|
shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_noqr.jpg")) |
||||||
|
|
||||||
|
bpy.context.scene.eyek.res_x = jpg_img.width |
||||||
|
bpy.context.scene.eyek.res_y = jpg_img.height |
||||||
|
bpy.context.scene.eyek.path_export_image = qr_path |
||||||
|
bpy.data.objects[pid_objname].select_set(True) |
||||||
|
bpy.data.objects['Empty'].select_set(True) |
||||||
|
bpy.context.view_layer.objects.active = bpy.data.objects[pid_objname] |
||||||
|
bpy.ops.eyek.exe() |
||||||
|
|
||||||
|
qr_img = Image.open(qr_path) |
||||||
|
jpg_img.paste(qr_img, (0, 0), qr_img) |
||||||
|
jpg_img.save(jpg_path, quality=90) |
||||||
|
shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_qr.jpg")) |
||||||
|
|
||||||
|
# 加入去灰、锐化 |
||||||
|
remove_gray_and_sharpening(jpg_path) |
||||||
|
|
||||||
|
#上传脚底板文件 -》 修改为移动脚底板文件 |
||||||
|
upload_jpg_mtl(pid, order_id, print_id) |
||||||
|
|
||||||
|
# plt.axis('equal') |
||||||
|
# plt.show() |
||||||
|
|
||||||
|
# 保存blend文件 |
||||||
|
# bpy.ops.wm.save_as_mainfile(filepath=f'{workdir}{pid}_{order_id}/{pid}_qr_end.blend') |
||||||
|
bpy.ops.wm.quit_blender() |
||||||
|
|
||||||
|
# 删除临时文件 |
||||||
|
print("workdirworkdir",workdir) |
||||||
|
shutil.rmtree(os.path.join(workdir, f'{pid}_{order_id}')) |
||||||
|
if only_one: |
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 运行{print_id}任务完成,退出程序') |
||||||
|
break |
||||||
|
else: |
||||||
|
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 运行{print_id}任务完成,继续运行下一个任务') |
||||||
|
print_id = '0' |
||||||
|
continue |
||||||
|
|
||||||
|
restart_current_process("blender -b -P fill_dm_code.py") |
||||||
|
|
||||||
|
#根据print_id 获取 短网址 |
||||||
|
def get_short_url(print_id): |
||||||
|
res = requests.get(f'{get_short_url_by_print_id}?print_id={print_id}') |
||||||
|
resCode = json.loads(res.text)['code'] |
||||||
|
#该笔订单的获取信息有误,可能信息还没有成功 |
||||||
|
if int(resCode) != 1000: |
||||||
|
print("获取短网址信息失败,延时1分钟,重新扔入脚底板队列,打印id-",print_id,"重新执行脚底板任务") |
||||||
|
time.sleep(60) |
||||||
|
#将pid 重新扔进队列 |
||||||
|
r.lpush("model:foot", print_id) |
||||||
|
#重新调用脚底板程序 |
||||||
|
return False |
||||||
|
return json.loads(res.text)['data'] |
||||||
|
|
||||||
|
|
||||||
|
@retry(stop_max_attempt_number=10, wait_fixed=2000) |
||||||
|
def upload_jpg_mtl(pid, order_id, print_id): |
||||||
|
try: |
||||||
|
print("移动脚底板文件") |
||||||
|
#判断文件夹是否存在,不存在则创建 |
||||||
|
if not os.path.exists(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/')): |
||||||
|
os.makedirs(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/'),mode=0o777, exist_ok=True) |
||||||
|
else: |
||||||
|
#移除路径下的文件 |
||||||
|
if os.path.exists(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/{pid}Tex1.jpg')): |
||||||
|
os.remove(os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/{pid}Tex1.jpg')) |
||||||
|
print(f"文件 '{pid}' 已成功删除。") |
||||||
|
|
||||||
|
shutil.move(os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg"),os.path.join(f'{resFilePath}/{pid}/foot_print_id_{print_id}/')) |
||||||
|
|
||||||
|
|
||||||
|
#print('生成贴图完成,开始上传...') |
||||||
|
# oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1.{print_id}.jpg', os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1.jpg")) |
||||||
|
# oss_client.put_object_from_file(f'objs/print/{pid}/{pid}.mtl', os.path.join(workdir,f'{pid}_{order_id}', f"{pid}.mtl")) |
||||||
|
# # oss_client.put_object_from_file(f'objs/print/{pid}/{pid}Tex1_noqr.jpg', os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_noqr.jpg")) |
||||||
|
|
||||||
|
print('更新状态为已生成脚底板二维码') |
||||||
|
res = requests.post(f'{upload_qr_position_url}?print_id={print_id}') |
||||||
|
# #记录日志 |
||||||
|
# logging.error(f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}-结果:pid:{pid}-print_id:{print_id} {str(res.text)}") |
||||||
|
print('更新返回状态:', f'{upload_qr_position_url}?print_id={print_id}', res.text) |
||||||
|
|
||||||
|
except Exception as e: |
||||||
|
|
||||||
|
print("迁移文件出 yichang ") |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
atexit.register(common.notify,"打印工厂-本地虚拟木脚底板处理程序已停止一个") |
||||||
|
low_y_limit = 25000 |
||||||
|
high_y_limit = 13000 |
||||||
|
|
||||||
|
get_qr_position_url = 'https://mp.api.suwa3d.com/api/printOrder/getFootCodePositionData' |
||||||
|
upload_qr_position_url = 'https://mp.api.suwa3d.com/api/printOrder/updateFootCodeStatus' |
||||||
|
get_pid_by_printid_url = 'https://mp.api.suwa3d.com/api/printOrder/getPidByPrintId' |
||||||
|
get_short_url_by_print_id = 'https://mp.api.suwa3d.com/api/footCode/qrcode' |
||||||
|
# get_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/getFootCodePositionData' |
||||||
|
# upload_qr_position_url = 'http://172.31.1.254:8199/api/printOrder/updateFootCodeStatus' |
||||||
|
# get_pid_by_printid_url = 'http://172.31.1.254:8199/api/printOrder/getPidByPrintId' |
||||||
|
|
||||||
|
|
||||||
|
r = redis.Redis(host='106.14.158.208', password='kcV2000', port=6379, db=6) |
||||||
|
# r = redis.Redis(host='172.31.1.254', password='', port=6379, db=6) |
||||||
|
AccessKeyId = 'LTAI5tSReWm8hz7dSYxxth8f' |
||||||
|
AccessKeySecret = '8ywTDF9upPAtvgXtLKALY2iMYHIxdS' |
||||||
|
Endpoint = 'oss-cn-shanghai.aliyuncs.com' |
||||||
|
Bucket = 'suwa3d-securedata' |
||||||
|
oss_client = oss2.Bucket(oss2.Auth(AccessKeyId, AccessKeySecret), Endpoint, Bucket) |
||||||
|
|
||||||
|
if platform.system() == 'Windows': |
||||||
|
workdir = 'E:\\print\\foot\\' |
||||||
|
else: |
||||||
|
workdir = '/data/datasets/foot/' |
||||||
|
|
||||||
|
#构建好的obj文件目录 |
||||||
|
sourceFilePath = '/data/datasets/print' |
||||||
|
#脚底板最终保存的路径 |
||||||
|
resFilePath = '/data/datasets/complate/objs' |
||||||
|
|
||||||
|
print("Usage: blender -b -P fill_dm_code.py") |
||||||
|
|
||||||
|
|
||||||
|
if len(sys.argv) == 5: |
||||||
|
print_ids = sys.argv[-1] |
||||||
|
else: |
||||||
|
print_ids = '0' |
||||||
|
|
||||||
|
print(f"print_ids--{print_ids}") |
||||||
|
for print_id in print_ids.split(','): |
||||||
|
main(workdir, r, print_id) |
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libMaterialXGenGlsl.so.1.38.6 |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libMaterialXGenShader.so.1.38.6 |
||||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libMaterialXRenderGlsl.so.1.38.6 |
||||||
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libMaterialXRenderHw.so.1.38.6 |
||||||
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libMaterialXRenderOsl.so.1.38.6 |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libOpenImageIO_Util.so.2.4.11 |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libboost_program_options.so.1.80.0 |
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1 @@ |
|||||||
|
libboost_serialization.so.1.80.0 |
||||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue