dongchangxi 9 months ago
parent
commit
9600ac4e29
  1. 223
      blender/build_resize_model.py
  2. 847
      blender/fill_dm_code_new.py
  3. 399
      build_print_obj.py

223
blender/build_resize_model.py

@ -0,0 +1,223 @@
from math import radians
import sys, platform, os, time, bpy, requests, json, bmesh, shutil
from PIL import Image
import platform
# if platform.system() == 'Windows':
# sys.path.append('e:\\libs\\')
# else:
# sys.path.append('/data/deploy/make3d/make2/libs/')
sys.path.append('/home/acprint/code/libs/')
import config, libs
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 fix_link_texture(pid):
# 修改obj中的mtl文件为pid_original.mtl
path = os.path.join(workdir, 'print', f'{pid}_{orderId}')
filename = os.path.join(path, f'{pid}_original.obj')
with open(filename, 'r') as f:
lines = f.readlines()
for i in range(len(lines)):
if lines[i].startswith('mtllib'):
lines[i] = f'mtllib {pid}_original.mtl\n'
break
with open(filename, 'w') as f:
f.writelines(lines)
f.close()
# 将pid.mtl文件复制为pid_original.mtl _decimate
shutil.copy(os.path.join(path, f'{pid}.mtl'), os.path.join(path, f'{pid}_original.mtl'))
shutil.copy(os.path.join(path, f'{pid}Tex1.jpg'), os.path.join(path, f'{pid}Tex1_decimate.jpg'))
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=100, optimize=True)
print('resize texture file to 50% success')
# 修改pid_original.mtl文件中的贴图为pid_old.jpg
with open(os.path.join(path, f'{pid}_original.mtl'), 'r') as f:
lines = f.readlines()
for i in range(len(lines)):
if lines[i].startswith('map_Kd'):
lines[i] = f'map_Kd {pid}Tex1_decimate.jpg\n'
break
with open(os.path.join(path, f'{pid}_original.mtl'), 'w') as f:
f.writelines(lines)
f.close()
def imagePiex(filePath,piex):
# 打开图片
image = Image.open(filePath)
# 设置新的最大宽度和高度
max_size = (piex, piex)
# 保持宽高比缩放图片
image.thumbnail(max_size)
# 保存调整后的图片
image.save(filePath)
def main():
start = time.time()
get_printsize_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/infoByOrderId'
upload_obj_volume_url = 'https://mp.gray.api.suwa3d.com/api/physical/add' # ?pid=1&order_id=1&faces=1&volume=1
print(f'{get_printsize_url}?order_id={orderId}')
res = requests.get(f'{get_printsize_url}?id={orderId}')
print('获取打印尺寸:', res.text)
if res.status_code == 200:
pid = res.json()['data']['pid']
path = os.path.join(workdir, 'print', f'{pid}_{orderId}')
filename = os.path.join(path, f'{pid}.obj')
imagePath = os.path.join(path, f'{pid}Tex1.jpg')
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'
print('正在处理:', filename)
bpy.ops.import_scene.obj(filepath=filename)
obj = bpy.context.selected_objects[0]
print('原始模型尺寸:', obj.dimensions)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
print('应用后模型尺寸:', obj.dimensions)
shutil.copy(filename, os.path.join(path, f'{pid}_original.obj'))
filename_original = os.path.join(path, f'{pid}_original.obj')
headcount = res.json()['data']['headcount']
for f in res.json()['data']['fileList']:
if "undefined" in f:
continue
try:
height = float(f.split('_')[-2][:-2]) * 10
except Exception as e:
print("eeee",e)
bpy.ops.wm.quit_blender()
return
obj = bpy.context.selected_objects[0]
bpy.context.view_layer.objects.active = obj
print(f'{f}处理前{height}mm模型尺寸: {obj.dimensions}')
scale = height / obj.dimensions.z
obj.scale = (scale, scale, scale)
bpy.ops.object.transform_apply(scale=True)
print(f'{f}处理后{height}mm模型尺寸: {obj.dimensions}')
if headcount == 1 and height <= 80:
pixs = 4096
faces_dest = 125000
if height <= 50:
pixs = 2048
faces_dest = 63000
print(f'减面到{faces_dest}')
faces_dest = faces_dest * 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")
# imagePiex(imagePath,pixs)
bpy.ops.export_scene.obj(filepath=os.path.join(path, f'{pid}.obj'))
if os.path.exists(os.path.join(path, f)):
os.remove(os.path.join(path, f))
os.rename(os.path.join(path, f'{pid}.obj'), os.path.join(path, f))
#config.oss_bucket.put_object_from_file(f'objs/print/{pid}/{f}', os.path.join(path, f))
# 重新加载模型,然后生成数字模型
bpy.ops.object.delete(use_global=False, confirm=False)
fix_link_texture(pid)
bpy.ops.import_scene.obj(filepath=filename_original)
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)
scale = 90 / obj.dimensions.y
obj.scale = (scale, scale, scale)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# if str(pid) == '120745': bpy.ops.wm.save_as_mainfile(filepath=os.path.join(path, f'{pid}_{orderId}.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')
tempWeight = obj_volume * 1.2
faces = len(obj.data.polygons)
print('faces:', faces)
upload_res = requests.get(f'{upload_obj_volume_url}?pid={pid}&order_id={orderId}&faces={faces}&volume={obj_volume}&headcount={headcount}&weight={tempWeight}')
print('上传模型体积:', upload_res.text)
# 生成数字模型
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.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.export_scene.obj(filepath=os.path.join(path, f'{pid}_decimate.obj'))
bpy.ops.export_scene.gltf(filepath=os.path.join(path, f'{pid}_decimate.glb'), export_format='GLB', export_apply=True, export_jpeg_quality=100)
config.oss_bucket.put_object_from_file(f'glbs/3d/{pid}.glb', os.path.join(path, f'{pid}_decimate.glb'))
bpy.ops.wm.quit_blender()
if __name__ == '__main__':
if platform.system() == 'Windows':
workdir = 'E:\\'
else:
workdir = '/data/datasets/'
if len(sys.argv) - (sys.argv.index("--") + 1) < 1:
print("Usage: blender -b -P resize_model.py -- <orderId>")
sys.exit(1)
orderId = sys.argv[sys.argv.index("--") + 1]
main()

847
blender/fill_dm_code_new.py

@ -0,0 +1,847 @@
import os, sys, bpy, math, time, platform, cairosvg, ppf.datamatrix, shutil, requests, json, redis, oss2, cv2,qrcode
from retrying import retry
import subprocess
import multiprocessing
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.gray.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)
prefix = f'objs/print/{pid}/'
#根据order_id 获取文件在 oss 上的存储路径
if order_id is not None:
tempURL = "https://mp.gray.api.suwa3d.com/api/order/getOssSuffixByOrderId?orderId="+str(order_id)
res = requests.get(tempURL)
resCode = res.json()['code']
if int(resCode) != 1000:
return -1
print(res.text)
if res.json()['data'] != "":
prefix = f'objs/print/{pid}/{res.json()["data"]}/{str(order_id)}/'
# 下载分2种情况,一种是第一次打印,下载标准{pid}.obj,{pid}.mtl,{pid}Tex1.jpg,另一种是重打或加打,obj文件名可以从oss上任意获取一个,但是mtl和jpg文件名是固定的
res = oss_client.get_object_to_file(f'{prefix}/{pid}.mtl', os.path.join(path, f'{pid}.mtl'))
last_modified = oss_client.get_object_meta(f"{prefix}/{pid}.mtl").last_modified
print(f'mtl文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}')
print(f'下载文件:{prefix}/{pid}.mtl,状态:{res.status}')
if oss_client.object_exists(f'{prefix}/{pid}Tex1.jpg'):
res = oss_client.get_object_to_file(f'{prefix}/{pid}Tex1.jpg', os.path.join(path, f'{pid}Tex1.jpg'))
last_modified = oss_client.get_object_meta(f"{prefix}/{pid}Tex1.jpg").last_modified
print(f'jpg文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}')
print(f'下载文件:{prefix}/{pid}Tex1.jpg,状态:{res.status}')
else:
res = oss_client.get_object_to_file(f'{prefix}/{pid}.jpg', os.path.join(path, f'{pid}Tex1.jpg'))
last_modified = oss_client.get_object_meta(f"{prefix}/{pid}.jpg").last_modified
print(f'jpg文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}')
print(f'下载文件:{prefix}/{pid}.jpg,状态:{res.status}')
if oss_client.object_exists(f'{prefix}/{pid}.obj'):
res = oss_client.get_object_to_file(f'{prefix}/{pid}.obj', os.path.join(path, f'{pid}.obj'))
last_modified = oss_client.get_object_meta(f"{prefix}/{pid}.obj").last_modified
print(f'obj文件最后修改时间:{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(last_modified))}')
print(f'下载文件:{prefix}/{pid}.obj,状态:{res.status}')
else:
prefix = f'{prefix}'
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
#进行图片分辨率的调整
# get_printsize_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/info'
# res = requests.get(f'{get_printsize_url}?id={order_id}')
# print('获取订单信息:', res.text)
# if res.status_code == 200:
# headcount = res.json()['data']['headcount']
# for f in res.json()['data']['fileList']:
# if "undefined" in f:
# continue
# try:
# height = float(f.split('_')[-2][:-2]) * 10
# except Exception as e:
# continue
# if headcount == 1 and height <= 80:
# pixs = 4096
# if height <= 5:
# pixs = 1024
# print(f"图片分辨率进行处理{pixs}")
# imagePiex(os.path.join(workdir, f'{pid}_{order_id}', f'{pid}Tex1.jpg'),pixs)
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 getInfoByPrintId(print_id):
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.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}')
#os.system(f'blender -b -P fill_dm_code.py')
return False
print("获取打印任务信息有问题,重新任务队列,打印id-",print_id,"重新执行脚底板任务,等待20s")
time.sleep(20)
#将pid 重新扔进队列
r.lpush("model:foot", print_id)
return False
return res.text
#获取订单的坐标信息,判断是否有,没有的话就重新生成坐标信息
def getQrPosition(print_id,pid,order_id):
#从云端获取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_position1:', res.text)
codeTemp = json.loads(res.text)['code']
if str(codeTemp) == "-1":
print(f"获取打印ID{print_id}的脚底板坐标失败,进行下一笔订单的处理")
return False
qr_position = json.loads(res.text)['data']['position_data']
if qr_position == '':
print(f"pid={pid},打印id={print_id} order_id={order_id},云端获取的坐标数据为空,进行重新计算")
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(f"pid={pid},打印id={print_id} order_id={order_id},重新获取qr_position",res.text)
print('从云端获取的qr_position3:', res.text)
if str(json.loads(res.text)['code']) == "-1":
print(f"pid={pid},打印id={print_id} order_id={order_id},重新获取qr_position 失败",res.text)
return False
qr_position = json.loads(res.text)['data']['position_data']
if qr_position == '':
#跳过该笔订单
print(f"pid={pid},打印id={print_id} order_id={order_id},重新获取qr_position 为空,跳过脚底板的处理,执行下一笔任务")
#移除该脚底板的面积处理
res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}')
return False
else:
qr_position = json.loads(qr_position)
if qr_position == "":
print("获取脚底坐标数据为空,重新任务队列,打印id-",print_id,"重新执行脚底板任务")
#将pid 重新扔进队列
r.lpush("model:foot", print_id)
return False
return qr_position
def main(workdir, r, print_id):
resText = getInfoByPrintId(print_id)
print(f"根据打印ID{print_id}获取订单信息-内容{resText}")
if resText == False:
print(f"根据打印ID{print_id}获取订单信息失败")
return
arrResText = json.loads(resText)
#订单的相关信息
use_foot_type = arrResText['data']['use_foot_type'] #构建类型
pid = arrResText['data']['pid'] #pid
order_id = arrResText['data']['order_id'] # order_id
#文件路劲
filename = os.path.join(workdir, f'{pid}_{order_id}', find_obj(pid, order_id))
print('导入obj文件:', filename)
#获取该笔订单的坐标
qr_position = getQrPosition(print_id,pid,order_id)
if qr_position == False:
return
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)
#休眠1秒
time.sleep(2)
#判断是否存在 qr_path,不存在就执行下一个任务
if not os.path.exists(qr_path):
#移除该脚底板的面积处理
print(f"pid={pid},打印id={print_id} order_id={order_id},生成的二维码为空,跳过脚底板的处理,执行下一笔任务")
res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}')
#os.system(f'blender -b -P fill_dm_code.py')
return
#在blender中贴上脚底板二维码的信息
# 导入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.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}')
#os.system(f'blender -b -P fill_dm_code.py')
bpy.ops.wm.quit_blender()
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_path是否存在,不存在就执行下一个任务
if not os.path.exists(qr_path):
#移除该脚底板的面积处理
res = requests.get(f'https://mp.gray.api.suwa3d.com/api/footCode/deleteByPid?pid={pid}')
#os.system(f'blender -b -P fill_dm_code.py')
bpy.ops.wm.quit_blender()
return
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)
shutil.copyfile(jpg_path, os.path.join(workdir,f'{pid}_{order_id}', f"{pid}Tex1_qr.jpg"))
#上传脚底板文件 -》 修改为移动脚底板文件
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}'))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 运行{print_id}任务完成,继续运行下一个任务')
# restart_current_process("blender -b -P fill_dm_code.py")
def imagePiex(filePath,piex):
# 打开图片
image = Image.open(filePath)
# 设置新的最大宽度和高度
max_size = (piex, piex)
# 保持宽高比缩放图片
image.thumbnail(max_size)
# 保存调整后的图片
image.save(filePath)
#根据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 ")
def worker(workdir, r, print_id):
"""每个子进程的工作函数,用于处理单个 print_id。"""
try:
main(workdir, r, print_id)
except Exception as e:
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f"处理 print_id {print_id} 时出错: {e}")
def process_print_ids(workdir, r, pool_size):
"""监控 Redis 队列并并行处理 print_id,同时控制进程池的大小。"""
# 创建进程池
pool = multiprocessing.Pool(processes=pool_size)
while True:
try:
# 如果队列为空,等待10秒后重试
if r.llen('model:foot') == 0:
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '队列为空,10秒后重试')
time.sleep(10)
continue
# 从队列中取出一个 print_id
print_id = r.lpop('model:foot')
if print_id is None:
continue
# 判断该 print_id 是否已经存在,避免重复处理
isHaveAlready = 0
for i in r.lrange('model:foot', 0, -1):
if i == print_id:
isHaveAlready = 1
break
if isHaveAlready == 1:
continue
# 如果 print_id 是唯一的,则将任务提交给进程池处理
print(f"正在处理 print_id: {print_id}")
pool.apply_async(worker, args=(workdir, r, print_id)) # 使用 apply_async 异步执行任务
except Exception as e:
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f"出现异常错误: {e}")
r = create_redis_connection()
continue
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)
if __name__ == '__main__':
atexit.register(common.notify,"打印工厂-本地虚拟木脚底板处理程序已停止一个")
low_y_limit = 25000
high_y_limit = 13000
get_qr_position_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/getFootCodePositionData'
upload_qr_position_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/updateFootCodeStatus'
get_pid_by_printid_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/getPidByPrintId'
get_short_url_by_print_id = 'https://mp.gray.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")
# # 控制进程池的大小
# pool_size = 2 # 设置最大并发进程数为4(根据需求调整)
# # 创建一个进程池
# pool = multiprocessing.Pool(processes=pool_size)
# # 如果传递了参数,则处理特定的 print_ids
# if len(sys.argv) == 5:
# print_ids = sys.argv[-1]
# for print_id in print_ids.split(','):
# main(workdir, r, print_id)
# else:
# # 启动进程池,监控队列并并行处理 print_id
# process_print_ids(workdir, r, pool_size)
if len(sys.argv) == 5:
print_ids = sys.argv[-1]
for print_id in print_ids.split(','):
main(workdir, r, print_id)
else:
while True:
try:
#判断队列是否为空
if r.llen('model:foot') == 0:
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '队列为空,10秒后重试')
time.sleep(10)
continue
#不为空,取出队列的第一个值
print_id = r.lpop('model:foot')
if print_id is None:
continue
#判断是否存在相同的值
isHaveAlready = 0
for i in r.lrange('model:foot', 0, -1):
if i == print_id:
isHaveAlready = 1
break
if isHaveAlready == 1:
continue
print_id = print_id.decode('utf-8')
#获取队列的长度
lengthQueue = r.llen('model:foot')
print(f"正在处理 print_id: {print_id}, 队列长度: {lengthQueue}")
#如果是唯一值, 则调用main函数
main(workdir, r, print_id)
except Exception as e:
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), '出现异常错误,5秒后重试',e)
r = create_redis_connection()
continue

399
build_print_obj.py

@ -0,0 +1,399 @@
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}/'
#根据order_id 获取文件在 oss 上的存储路径
if order_id is not None:
tempURL = "https://mp.gray.api.suwa3d.com/api/order/getOssSuffixByOrderId?order_id="+str(order_id)
print(tempURL)
res = requests.get(tempURL)
print(res.json())
resCode = res.json()['code']
if int(resCode) != 1000:
return -1
print(res.text)
if res.json()['data'] != "":
prefix = f'objs/print/{pid}/{res.json()["data"]}/{str(order_id)}'
print(prefix)
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix)
#判断是否有值,没有值的话就从老的路径再获取一次数据
if len(list(filelist)) == 0:
filelist = oss2.ObjectIteratorV2(oss_client, prefix=f'objs/print/{pid}/')
else:
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" and filename != str(pid)+"_RB_B_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.gray.api.suwa3d.com/api/printOrder/infoByOrderId'
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
#根据 buy_type 判断 是否是冰箱贴 或者徽章
buyType = str(res['buy_type'])
if buyType == "2" or buyType == "3" or buyType == "4":
#塞入到no_resize 队列
r.lpush("model:noresize", orderId)
return
pid = str(res['pid'])
if pid == "88985":
return
if pid == "":
return
#创建正在处理的文本内容
creatDoingLog(orderId)
print_type = 1
digital_type = 1 # 0: 只有手办 1: 只有数字模型 2: 手办+数字模型
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), f'orderId:{orderId} pid:{pid} 生成待打印模型 start', )
isFindObj = False
print("AAAAAAAAAAAAAAAAAA")
downloadRes = down_obj_fromoss(pid, print_type, orderId,download_flag="print_build")
print("downloadRes",downloadRes)
if downloadRes == -1:
print("下载错误")
return
# 获取程序运行当前目录
resize_py_path = os.path.join(os.getcwd(), 'blender', 'build_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 gray_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))
print(f"复制文件路径-{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 gray_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:
#构建打印文件
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.gray.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingPlatformCheckingStatus'
update_team_check_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateStatusToWaitingTeamCheckingStatus'
update_status_printstatus_url = 'https://mp.gray.api.suwa3d.com/api/customerP3dLog/updateBuildPrintModelStatus'
update_makeprintobj_status_url = 'https://mp.gray.api.suwa3d.com/api/printOrder/updateMakePrintObjSucceed'
getRepairInfo_url = 'https://repair.gray.api.suwa3d.com/api/modelRepairOrder/teamCheckGLBInfo'
update_repair_status_url = 'https://repair.gray.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)
Loading…
Cancel
Save