新的信息

This commit is contained in:
dongchangxi committed 2024-09-02 16:52:37 +08:00
1 parent c66fc76e10
commit 345790cb05
51 files changed
+5187 -61

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
import platform,sys,redis,time,requests,json,atexit
sys.path.append('../libs/')
import config,libs,libs_db_temp,common
import os, sys, time, argparse, requests, json, re, oss2, probreg
import bpy, bmesh
import open3d as o3d
import numpy as np
import cupy as cp
from mathutils import Matrix
import math
import logging
import matplotlib.pyplot as plt
#获取真实身高
def get_real_height(input_path):
get_real_height_url = 'https://mp.api.suwa3d.com/api/physical/infoByPid'
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}: Getting real height from {input_path}')
pid = re.findall(r'(\d+)', input_path)[0]
print(f'pid: {pid}')
res = requests.get(get_real_height_url, params={'pid': pid})
res = json.loads(res.text)
if res['code'] == -1:
print(f'Error: {res["message"]}, return default height 160')
return 160
height = res['data']['height']
if height == 0:
print(f'Error: height=0, return default height 160')
return 160
print(f'height: {height}')
return height
def base_fix(input_path):
if not os.path.exists(input_path):
print(f'Error: {input_path} does not exist.')
sys.exit(1)
bpy.ops.wm.read_homefile()
bpy.ops.object.delete(use_global=False, confirm=False)
bpy.ops.wm.obj_import(filepath=input_path)
bpy.context.scene.unit_settings.scale_length = 1
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)
# 脚底贴地
bpy.ops.object.align(align_mode='OPT_1', relative_to='OPT_1', align_axis={'Z'})
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
real_height = get_real_height(input_path)
print(obj.dimensions)
scale = real_height / obj.dimensions[2] / 100 # 除以100是因为单位是厘米
bpy.context.object.scale = (scale, scale, scale)
bpy.context.object.rotation_euler = (0, 0, 0)
bpy.ops.object.origin_set(type='ORIGIN_CENTER_OF_VOLUME', center='MEDIAN')
bpy.context.object.location[0] = 0 # 移动到特定位置(1m, 1m)是为了让human3d可以正确识别
bpy.context.object.location[1] = 0
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
# # bpy.ops.wm.save_mainfile(filepath=args.input_path.replace('.obj', '.blend'))
output_file = input_path.replace('.obj', '_baseFixed.obj')
bpy.ops.wm.obj_export(filepath=output_file)
return obj
def readTask():
# #读取数据库的任务
# pid = libs_db_temp.get_task_by_level()
# pid = str(pid)
# #下载pid的对应的oss端的文件
# tempName = libs.down_obj_from_oss_for_fix("E://obj_fix", pid, "print")
# if tempName == "":
# print("找不到对应的obj文件")
# #记录状态为异常 -1
# libs_db_temp.update_fix_status(pid,-1)
# return ""
# #记录状态为处理中
# libs_db_temp.update_fix_status(pid,1)
# #处理逻辑
# return pid
pid=7013
base_fix(f'E://obj_fix/{pid}/{pid}.obj')
# print(pid)
# print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+"-读取数据库任务")
#程序主入口
if __name__ == '__main__':
#atexit.register(common.notify,"处理数据校准任务已经停止")
readTask()
# while True:
# print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())+"-开始执行任务")
# pid = readTask()
# if pid != "":
# break
+32 -4
View File
@@ -1,4 +1,4 @@
import os, sys, time, bpy, bmesh, shutil
import os, sys, time, bpy, bmesh, shutil,requests,json
import platform
if platform.system() == 'Windows':
sys.path.append('e:\\libs\\')
@@ -64,16 +64,35 @@ def cal_weight(obj, size):
model_info['volume'] = round(bm.calc_volume() / 1000)
model_info['weight'] = round(model_info['volume'] * 1.226)
print(f'{size/10}cm:体积 {model_info["volume"]}cm³, 克重 {model_info["weight"]}g')
return model_info["weight"]
def main(action, pid, sizes):
def main(action, pid, sizes,orderId=0):
libs.down_obj_from_oss(config.workdir, pid, action)
obj = reload_obj(pid, action)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
print(f'模型{pid}的体积与克重估算信息:')
arrData = {}
for size in sizes:
size = float(size)
cal_weight(obj, size)
weight = cal_weight(obj, size)
size = str(size/10)+"cm"
arrData[size] = str(weight)+"g"
#请求接口进行更新数据
arrParams = {"pid":pid,"order_id":orderId}
if action == "auto":
arrParams["auto_weight"] = json.dumps(arrData)
if action == "print":
arrParams["print_weight"] = json.dumps(arrData)
#发起请求
print(f"请求的参数-{arrParams}")
url = "https://mp.api.suwa3d.com/api/physical/addPhysicalWeight"
res = requests.post(url,data=arrParams)
print('res:', res.text)
if __name__ == '__main__':
sizes = (90, 120, 150, 180)
@@ -83,10 +102,19 @@ if __name__ == '__main__':
pids = sys.argv[2].split(',')
for pid in pids:
main(action, pid, sizes)
elif len(sys.argv) == 5:
action = sys.argv[1]
pids = sys.argv[2].split(',')
sizes = sys.argv[3].split(',')
orderId = sys.argv[4]
for pid in pids:
main(action, pid, sizes,orderId)
print('Usage: python cal_weight.py print <pids> order_id 自定义尺寸/默认四个尺寸就不填')
elif len(sys.argv) == 4:
action = sys.argv[1]
pids = sys.argv[2].split(',')
sizes = sys.argv[3].split(',')
for pid in pids:
main(action, pid, sizes)
print('Usage: python cal_weight.py print <pids> 自定义尺寸/默认四个尺寸就不填')
print('Usage: python cal_weight.py print <pids> order_id 自定义尺寸/默认四个尺寸就不填')
+43
View File
@@ -0,0 +1,43 @@
import os,sys,requests,json
if __name__ == "__main__":
#获取参数
args = sys.argv
print("args:",args)
if len(args) != 2:
print("参数错误")
exit()
pid = args[1]
print("pid",pid)
#根据pid查询order_id的集合
url="https://mp.api.suwa3d.com/api/physical/listOrderIdByPid?pid="+str(pid)
res=requests.get(url)
if res.status_code != 200:
print('获取失败,程序退出')
exit()
res = json.loads(res.text)
print("res",res)
if res["code"] != 1000:
exit()
orderIds = res['data']
print("orderIds:",orderIds)
#循环处理
for orderId in orderIds:
#os.system(f"python D:/make2/tools/cal_weight.py print {pid} {orderId}")
url = "https://mp.api.suwa3d.com/api/physical/infoModelSizeByOrderId?order_id="+str(orderId)
print("url:",url)
res = requests.get(url)
if res.status_code != 200:
print('获取失败,程序退出')
continue
print("res",res)
res = json.loads(res.text)
print("res",res)
#获取到model_size
modelSize = res['data']['model_size']
pid = res['data']['pid']
print(f"执行脚本 python D:/make2/tools/cal_weight.py auto {pid} {modelSize} {orderId}")
os.system(f"python D:/make2/tools/cal_weight.py auto {pid} {modelSize} {orderId}")
# python D:/make2/tools/get_weight_by_pid.py
+6
View File
@@ -0,0 +1,6 @@
1. pip install -r requirements.txt安装requirements.txt所需的python包 (python3.10)
2. 使用python optimize_model.py -ip 输入的obj路径 -op 输出的obj路径
python optimize_model.py -ip "D:\finished\190104\output\190104.obj" -op "D:\finished\190104\output\190104_new.obj"
+381
View File
@@ -0,0 +1,381 @@
import os, time, json, requests, shutil, oss2, psutil
from tqdm import tqdm
from PIL import Image, ImageEnhance
import config,libs_db,common
import threading
from concurrent.futures import ThreadPoolExecutor
def find_blender_bin_path():
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)
def resize_photos(photo_path, ratio=0.5):
for filename in os.listdir(photo_path):
if filename.endswith('.jpg'):
img = Image.open(os.path.join(photo_path, filename))
img = rotate_image(img)
w, h = img.size
img = img.resize((int(w * ratio), int(h * ratio)))
img.save(os.path.join(photo_path, filename))
def rotate_image(image):
# 检查图像的EXIF数据是否包含方向信息
try:
exif = image._getexif()
orientation = exif.get(0x0112)
except:
orientation = None
# 根据方向信息旋转图像
if orientation == 3:
image = image.rotate(180, expand=True)
elif orientation == 6:
image = image.rotate(270, expand=True)
elif orientation == 8:
image = image.rotate(90, expand=True)
return image
def get_ps_adjust_photo_para(psid):
res = requests.get(config.urls['get_ps_adjust_photo_para_url'], params={'id': psid})
print(res.json())
paras = res.json()['data']
brightness_factor, saturation_factor, temperature_factor = float(paras['brightness']), float(paras['saturation']), float(paras['colorTemperature'])
return brightness_factor, saturation_factor, temperature_factor
def adjust_photos(workdir, pid):
def adjust_brightness(image, brightness_factor):
if brightness_factor == 1 or brightness_factor == 0 :
return image
enhancer = ImageEnhance.Brightness(image)
adjusted_image = enhancer.enhance(brightness_factor)
return adjusted_image
def adjust_saturation(image, saturation_factor):
if saturation_factor == 1:
return image
enhancer = ImageEnhance.Color(image)
adjusted_image = enhancer.enhance(saturation_factor)
return adjusted_image
def adjust_temperature(image, temperature_factor):
if temperature_factor == 1:
return image
r, g, b = image.split()
r = r.point(lambda i: i * temperature_factor)
adjusted_image = Image.merge("RGB", (r, g, b))
return adjusted_image
if not os.path.exists(os.path.join(workdir, pid, 'photo2')):
print(f"Directory {os.path.join(workdir, pid, 'photo2')} does not exist")
return False
psid = getPSid(pid)
brightness_factor, saturation_factor, temperature_factor = get_ps_adjust_photo_para(psid)
if (brightness_factor == 1 and saturation_factor == 1 and temperature_factor == 1):
print("No need to adjust")
return False
if os.path.exists(os.path.join(workdir, pid, 'photo3')):
print(f'{os.path.join(workdir, pid, "photo3")}目录已存在,跳过')
return
os.makedirs(os.path.join(workdir, pid, 'photo3'), exist_ok=True)
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 开始调整图片曝光...')
start_time = time.time()
for filename in os.listdir(os.path.join(workdir, pid, 'photo2')):
if filename.endswith(".jpg"):
# print(f"Adjusting {filename}:brightness={brightness_factor}, saturation={saturation_factor}, temperature={temperature_factor}")
image = Image.open(os.path.join(workdir, pid, 'photo2', filename))
image = rotate_image(image)
brightened_image = adjust_brightness(image, brightness_factor)
saturated_image = adjust_saturation(brightened_image, saturation_factor)
adjusted_image = adjust_temperature(saturated_image, temperature_factor)
adjusted_image.save(os.path.join(workdir, pid, 'photo3', filename))
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} 图片曝光调整完成,共费时{diff_time(start_time)}')
return True
def getPSid(pid):
res = requests.get(config.urls['get_psid_url'], params={'pid': pid})
print('get_psid_url:', res.url)
print('res:', res.text)
res = json.loads(res.text)
return str(res['data'])
def getHeadCount(pid):
res = requests.get(config.urls['get_printinfo_url'], params={'id': pid})
print('get_printinfo_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 get_ps_type(pid):
# return 1:圆形影棚 2:方形影棚
res = requests.get(config.urls['get_ps_type_url'], params={'pid': pid})
return res.json()['data']['type']
def find_valid_camera_on_oss(pid):
if get_ps_type(pid) == 1:
print('当前拍照影棚为:圆形影棚')
cameras = (103, 93, 113)
else:
print('当前拍照影棚为:方形影棚')
cameras = (74, 64, 84)
find_camera = 0
for camera in cameras:
objectkey = f'photos/{pid}/photo2/{camera}_8.jpg'
find = config.oss_bucket.object_exists(objectkey)
if find:
find_camera = camera
break
print('找到有效正脸相机:', find_camera)
if find_camera == 0:
print('{cameras}没有找到照片,程序退出')
exit(1)
return find_camera
def aliyun_face(pid):
high = False
style = 'imm/detectface'
camera = find_valid_camera_on_oss(pid)
objectkey = f'photos/{pid}/photo2/{camera}_8.jpg'
try:
res = config.oss_bucket.get_object(objectkey, process=style)
except oss2.exceptions.NoSuchKey:
print('没有找到文件:', objectkey)
return high
res = json.loads(res.read())
if res['success']:
if res['Faces'] is None:
print('no face')
return None
else:
print('faces num:', len(res['Faces']))
for face in res['Faces']:
print('-' * 20)
print('face_id:', face['FaceId'])
print('gender:', face['Gender'])
print('age:', face['Age'])
if face['Gender'] == 'FEMALE' and face['Age'] < 22: high = True
if face['Gender'] == 'MALE' and face['Age'] < 15: high = True
else:
print('face detect failed...')
return high
def down_obj_from_oss(workdir, pid, action):
if os.path.exists(os.path.join(workdir, action, pid)):
print(f'目录{os.path.join(workdir, action, pid)}已存在,跳过')
return
else:
os.makedirs(os.path.join(workdir, action, pid))
# 根据前缀获取文件列表
prefix = f'objs/{action}/{pid}/'
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
print('正在下载:', prefix)
obj_filename = ""
for file in filelist:
filename = file.key.split('/')[-1]
if filename.endswith('.obj'):
obj_filename = filename
# print('正在下载:', file.key)
localfile = os.path.join(workdir, action, pid, filename)
config.oss_bucket.get_object_to_file(file.key, localfile)
return obj_filename
def set_photos_join_type(workdir, pid, photoN, mesh = '0', texture='0'):
photoN_path = os.path.join(workdir, pid, photoN)
for xmp in os.listdir(photoN_path):
if xmp.endswith('.xmp'):
xmp_path = os.path.join(photoN_path, xmp)
with open(xmp_path, 'r') as f:
lines = f.readlines()
lines = [line.replace('xcr:InMeshing="0"', f'xcr:InMeshing="{mesh}"') for line in lines]
lines = [line.replace('xcr:InMeshing="1"', f'xcr:InMeshing="{mesh}"') for line in lines]
lines = [line.replace('xcr:InTexturing="0"', f'xcr:InTexturing="{texture}"') for line in lines]
lines = [line.replace('xcr:InTexturing="1"', f'xcr:InTexturing="{texture}"') for line in lines]
with open(xmp_path, 'w') as f:
f.writelines(lines)
def set_photo_join_type(workdir, pid, photoN, camera_id, mesh = '0', texture='0'):
if photoN == 'photo1':
filename = os.path.join(workdir, pid, photoN, f'{camera_id}_1.xmp')
else:
filename = os.path.join(workdir, pid, photoN, f'{camera_id}_8.xmp')
with open(filename, 'r') as f:
lines = f.readlines()
lines = [line.replace('xcr:InMeshing="0"', f'xcr:InMeshing="{mesh}"') for line in lines]
lines = [line.replace('xcr:InMeshing="1"', f'xcr:InMeshing="{mesh}"') for line in lines]
lines = [line.replace('xcr:InTexturing="0"', f'xcr:InTexturing="{texture}"') for line in lines]
lines = [line.replace('xcr:InTexturing="1"', f'xcr:InTexturing="{texture}"') for line in lines]
with open(filename, 'w') as f:
f.writelines(lines)
def down_from_oss(oss_client, workdir, pid, per=100,photoPath=""):
start_time = time.time()
path = os.path.join(workdir, pid)
if os.path.exists(path):
print(f"Directory {path} already exists, skip")
return
os.makedirs(os.path.join(path, 'photo1'))
os.makedirs(os.path.join(path, 'photo2'))
psid = getPSid(pid)
# 根据前缀获取文件列表
prefix = f'photos/{pid}/'
filelist = oss2.ObjectIteratorV2(oss_client, prefix=prefix)
for file in tqdm(filelist):
filename = file.key.split('/')[-1]
localfile = ""
# print('正在下载:', file.key)
if photoPath == "":
if filename.endswith('_1.jpg'):
localfile = os.path.join(path, 'photo1', filename)
else:
localfile = os.path.join(path, 'photo2', filename)
else:
if photoPath=="1":
if filename.endswith('_1.jpg'):
localfile = os.path.join(path, 'photo1', filename)
else:
if filename.endswith('_8.jpg'):
localfile = os.path.join(path, 'photo2', filename)
if localfile == "":
continue
style = f'image/resize,p_{per}'
if per == 100:
oss_client.get_object_to_file(file.key, localfile)
else:
oss_client.get_object_to_file(file.key, localfile, process=style)
#判断localfile 是否有包含 photo2
#遍历处理photo2的数数据
# if str(psid) == "96" or str(pid) == "118994":
# path = os.path.join(workdir, pid, 'photo2')
# files = []
# for fileName in os.listdir(path):
# if ".jpg" in fileName:
# files.append(path+"\\"+fileName)
# beginTime = time.time()
# with ThreadPoolExecutor(max_workers=6) as executor:
# executor.map(process_image, files)
# print(f'{localfile}灰度处理费时{diff_time(beginTime)}')
print(f'{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())} pid: {pid} 图片下载完成, 共费时{diff_time(start_time)}')
#灰度处理图片
def process_image(localfile):
if ".jpg" in localfile:
common.remove_gray_and_sharpening(localfile)
def get_defineDistances(psid):
res = ''
distances = libs_db.get_floor_sticker_distances(psid).split(';')
print("distances",distances)
for d in distances:
p1, p2, distance = d.split(' ')
res = res + f' -defineDistance {p1} {p2} {distance}'
return res.strip()
def is_running(psname):
for p in psutil.process_iter(['name']):
if psname.strip() in p.info['name']:
return True
return False
def diff_time(start_time):
# 按照分:秒的方式返回时间差
end_time = time.time()
diff = end_time - start_time
m, s = divmod(diff, 60)
return f'{int(m)}分{int(s)}秒'
def down_obj_from_oss_for_fix(workdir, pid, action):
if os.path.exists(os.path.join(workdir, action, pid)):
print(f'目录{os.path.join(workdir, action, pid)}已存在,跳过')
return
else:
os.makedirs(os.path.join(workdir, action, pid))
# 根据前缀获取文件列表
prefix = f'objs/{action}/{pid}/'
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
print('正在下载:', prefix)
obj_filename = ""
for file in filelist:
filename = file.key.split('/')[-1]
if filename.endswith(f'{pid}.obj') == False:
continue
if filename.endswith('.obj'):
obj_filename = filename
# print('正在下载:', file.key)
localfile = os.path.join(workdir, action, pid, filename)
config.oss_bucket.get_object_to_file(file.key, localfile)
return obj_filename
def down_obj_from_oss_for_repeat_texture(workdir, pid, action):
print(os.path.join(workdir, pid))
if not os.path.exists(os.path.join(workdir, pid)):
print("目录不存在,无法处理")
return "error"
#判断是否有专门新建的目录用来重新贴图
newPath = os.path.join(workdir, pid,'repeat_texture','completion_cloud')
if not os.path.exists(newPath):
os.makedirs(newPath)
# 根据前缀获取文件列表
prefix = f'objs/{action}/{pid}/ai/repeat_texture/completion_cloud'
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
print('正在下载:', prefix)
for file in filelist:
filename = file.key.split('/')[-1]
if filename == "":
continue
localfile = os.path.join(newPath, filename)
config.oss_bucket.get_object_to_file(file.key, localfile)
return newPath
def down_obj_from_oss_auto(pid,action,pathStr):
#pathStr = os.path.join(workdir, str(pid)+"_ai")
if not os.path.exists(pathStr):
os.makedirs(pathStr)
# 根据前缀获取文件列表
prefix = f'objs/{action}/{pid}'
filelist = oss2.ObjectIteratorV2(config.oss_bucket, prefix=prefix)
print('正在下载:', prefix)
for file in filelist:
filename = file.key.split('/')[-1]
if filename == "":
continue
if filename != f"{pid}.jpg" and filename != f"{pid}.obj" and filename != f"{pid}.mtl":
continue
print(filename)
localfile = os.path.join(pathStr, filename)
config.oss_bucket.get_object_to_file(file.key, localfile)
+31
View File
@@ -0,0 +1,31 @@
import paramiko,time,sys
def main_optimize_model(pid):
try:
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在known_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
print('start connect')
# 连接服务器
ssh.connect('connect.bjc1.seetacloud.com', username='root', password='QoQA8q3Ds2VB', port=33733)
print('connect success')
# 使用nohup执行命令,并将输出重定向到文件
command = f'nohup seg_python /data/code/optimize_model_xj/optimize_model.py -pid {pid} >{pid}.log 2>&1&'
stdin, stdout, stderr = ssh.exec_command(command)
# 打印命令是否启动成功
print("Command executed")
finally:
# 确保连接被关闭
if ssh:
time.sleep(1)
ssh.close()
if __name__ == '__main__':
if len(sys.argv) == 2:
pid = sys.argv[1]
main_optimize_model(pid)
+244
View File
@@ -0,0 +1,244 @@
import bpy
import sys
import open3d as o3d
import numpy as np
from sklearn.neighbors import NearestNeighbors
import os
import argparse
def estimate_point_curvatures(point_cloud, k=30):
point_cloud_points = np.asarray(point_cloud.points)
# Initialize the nearest neighbors finder
nn = NearestNeighbors(n_neighbors=k)
nn.fit(point_cloud_points)
curvatures = {i: 0 for i in range(len(point_cloud.points))}
for i, point in enumerate(point_cloud_points):
# Find the k-nearest neighbors (including the point itself)
distances, indices = nn.kneighbors([point])
neighbors = point_cloud_points[indices[0]]
# Compute the covariance matrix
covariance_matrix = np.cov(neighbors - neighbors.mean(axis=0), rowvar=False)
# Compute the eigenvalues (sorted)
eigenvalues, _ = np.linalg.eigh(covariance_matrix)
eigenvalues.sort()
# Calculate curvature using the smallest eigenvalue
curvature = eigenvalues[0] / sum(eigenvalues)
curvatures[i] = curvature
return curvatures
def compute_adjacent_normal_angle_variance(pcd, pcd_normal, distance_scale):
kdtree = o3d.geometry.KDTreeFlann(pcd)
distances = []
for i in range(len(pcd.points)):
[k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2)
dist = dist[1:] # distances to the actual neighbors
distances.append(dist)
mean_distance = np.mean(distances)
adjacent_normal_angles_variance = {i: 0 for i in range(len(np.asarray(pcd.points)))}
for i, point in enumerate(pcd.points):
# Search for the k-nearest neighbors of the point, up to 1500
k, idx, dist = kdtree.search_knn_vector_3d(point, 200)
idx = np.asarray(idx)
indices = idx[dist < mean_distance * distance_scale]
current_normal = pcd_normal[i]
# Calculate dot products between the current normal and its neighbors' normals
dot_products = np.dot(pcd_normal[indices], current_normal)
# Handle any potential NaN values in dot products
valid_dot_products = np.isfinite(dot_products)
dot_products = dot_products[valid_dot_products]
# Clip dot product values to valid range for arccos
dot_products = np.clip(dot_products, -1.0, 1.0)
# Compute angles in degrees and adjust for any NaNs which might be due to numerical errors
angles_array = np.degrees(np.arccos(dot_products))
# Set the angle with itself to zero since it's not meaningful
angles_array[indices == i] = 0
# Calculate the mean angle excluding the zero for the current point
valid_angles = angles_array[indices != i]
if valid_angles.size > 0:
adjacent_normal_angles_variance[i] = np.std(valid_angles)
else:
adjacent_normal_angles_variance[i] = 0 # Default to 0 if no valid neighbors exist
return adjacent_normal_angles_variance
def compute_adjacent_point_vector_degrees_mean(pcd, pcd_normal, distance_scale):
kdtree = o3d.geometry.KDTreeFlann(pcd)
distances = []
for i in range(len(pcd.points)):
[k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2)
dist = dist[1:] # distances to the actual neighbors
distances.append(dist)
mean_distance = np.mean(distances)
adjacent_point_vector_degrees_mean = {i: 0 for i in range(len(np.asarray(pcd.points)))}
for i, point in enumerate(pcd.points):
# Search for the k-nearest neighbors of the point, up to 200
k, idx, dist = kdtree.search_knn_vector_3d(point, 200)
idx = np.asarray(idx)
indices = idx[dist < mean_distance * distance_scale]
indices = indices[indices != i]
# Retrieve the normal of the current point
current_normal = pcd_normal[i]
adjacent_point_vector = np.asarray(pcd.points)[indices] - point
adjacent_point_vector_cos = np.dot(adjacent_point_vector, current_normal) / np.linalg.norm(adjacent_point_vector, axis = 1) * 1
valid_adjacent_point_vector_cos = np.isfinite(adjacent_point_vector_cos)
adjacent_point_vector_cos = adjacent_point_vector_cos[valid_adjacent_point_vector_cos]
if len(adjacent_point_vector_cos) > 0:
adjacent_point_vector_degrees = np.degrees(np.arccos(adjacent_point_vector_cos))
adjacent_point_vector_mean_degrees = np.mean(adjacent_point_vector_degrees)
adjacent_point_vector_degrees_mean[i] = adjacent_point_vector_mean_degrees
else:
adjacent_point_vector_degrees_mean[i] = 0
return adjacent_point_vector_degrees_mean
def spread_point(pcd, selected_index, distance_scale, count = False):
kdtree = o3d.geometry.KDTreeFlann(pcd)
distances = []
for i in range(len(pcd.points)):
[k, idx, dist] = kdtree.search_knn_vector_3d(pcd.points[i], 2)
dist = dist[1:] # distances to the actual neighbors
distances.append(dist)
mean_distance = np.mean(distances)
if count is False:
spread_selected_index = set()
for i in selected_index:
k, idx, dist = kdtree.search_knn_vector_3d(pcd.points[i], 200)
idx = np.asarray(idx)
dist = np.asarray(dist)
indices = idx[dist < mean_distance * distance_scale]
spread_selected_index.update(indices)
spread_selected_index = np.array(list(spread_selected_index))
else:
spread_selected_index = {i: 0 for i in range(len(np.asarray(pcd.points)))}
for i in selected_index:
k, idx, dist = kdtree.search_knn_vector_3d(pcd.points[i], 200)
idx = np.asarray(idx)
dist = np.asarray(dist)
indices = idx[dist < mean_distance * distance_scale]
for indice in indices:
spread_selected_index[indice] += 1
return spread_selected_index
def ApplySmoothEffort(object, selected_vertice_indices = None, factor = 0.5, iterations = 30):
if selected_vertice_indices is not None:
# Assuming 'mesh' is already defined, e.g., mesh = bpy.context.object.data
selected_vertices_index = set() # Initialize an empty set to store unique vertex indices
# Iterate over each index in the list of polygon indices
for idx in selected_vertice_indices:
polygon = object.data.polygons[idx]
for vert_idx in polygon.vertices:
selected_vertices_index.add(vert_idx)
# Switch to Edit mode
bpy.ops.object.mode_set(mode='EDIT')
# Ensure we're dealing with the latest data
bpy.ops.object.mode_set(mode='OBJECT')
for i, vertex in enumerate(object.data.vertices):
vertex.select = False
if i in selected_vertices_index:
vertex.select = True
# Update the object to reflect changes
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.object.mode_set(mode='OBJECT')
# Add a Smooth modifier and set it to use the vertex group
smooth_mod = object.modifiers.new(name="SmoothMod", type='SMOOTH')
if selected_vertice_indices is not None:
# Create a new vertex group
group = object.vertex_groups.new(name='Smooth Group')
# Add selected vertices to the vertex group
group.add([v.index for v in object.data.vertices if v.select], weight=1.0, type='ADD')
smooth_mod.vertex_group = 'Smooth Group'
smooth_mod.factor = factor
smooth_mod.iterations = iterations
# bpy.ops.object.modifier_apply(modifier="SmoothMod")
# if selected_vertice_indices is not None:
# object.vertex_groups.clear()
def process(input_mesh_path, output_mesh_path):
# Select and delete all objects in the current scene to start fresh
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(confirm=False)
# Path to the OBJ file to be imported
bpy.ops.wm.obj_import(filepath=input_mesh_path)
# Set the current object to the newly imported mesh
current_obj = bpy.context.object
# Retrieve the mesh data from the current object
mesh_data = current_obj.data
bpy.context.view_layer.update()
# Convert Blender mesh vertices to a NumPy array
vertices_array = np.array([vertex.co[:] for vertex in mesh_data.vertices])
# Convert Blender mesh polygons to a NumPy array of triangles
triangles_array = np.array([polygon.vertices[:] for polygon in mesh_data.polygons])
# Create an Open3D mesh object from the arrays
open3d_mesh = o3d.geometry.TriangleMesh()
open3d_mesh.vertices = o3d.utility.Vector3dVector(vertices_array)
open3d_mesh.triangles = o3d.utility.Vector3iVector(triangles_array)
open3d_mesh.compute_triangle_normals()
# Retrieve and store the normals as a NumPy array
triangle_normals_array = np.asarray(open3d_mesh.triangle_normals)
# Compute centroids for each face of the mesh
triangle_centroids_array = np.array([
np.mean(vertices_array[triangle], axis=0)
for triangle in open3d_mesh.triangles
])
# Create a point cloud of face centroids
pcd_triangle_centroid = o3d.geometry.PointCloud()
pcd_triangle_centroid.points = o3d.utility.Vector3dVector(triangle_centroids_array)
point_curvatures = estimate_point_curvatures(pcd_triangle_centroid, 30)
np_point_curvatures = np.array(list(point_curvatures.values()))
point_curvatures_index = np.arange(len(np_point_curvatures))[np_point_curvatures > 0.25]
spread_point_curvatures_index = spread_point(pcd_triangle_centroid, point_curvatures_index, 50)
ApplySmoothEffort(current_obj, spread_point_curvatures_index, 0.5, 30)
adjacent_normal_angle_variance = compute_adjacent_normal_angle_variance(pcd_triangle_centroid, triangle_normals_array, 40)
adjacent_point_vector_degrees_mean = compute_adjacent_point_vector_degrees_mean(pcd_triangle_centroid, triangle_normals_array, 40)
array_adjacent_point_vector_degrees_mean = np.array(list(adjacent_point_vector_degrees_mean.values()))
array_adjacent_normal_angle_variance = np.array(list(adjacent_normal_angle_variance.values()))
use_data = np.where(array_adjacent_point_vector_degrees_mean < 90, array_adjacent_normal_angle_variance, 0)
# Calculate the minimum and maximum values in the angle array, ignoring NaNs.
min_angle = np.nanmin(use_data)
max_angle = np.nanmax(use_data)
# Perform min-max normalization on the angles to scale them between 0 and 1.
# This normalization helps in comparing values on a common scale.
normalized_angles = (use_data - min_angle) / (max_angle - min_angle)
first_select_thread = normalized_angles > 0.2
first_select_thread_number = first_select_thread.astype(np.float64)
thread_normalized_angles_index = np.arange(len(first_select_thread))[first_select_thread]
spread_point_count = spread_point(pcd_triangle_centroid, thread_normalized_angles_index, 3, count=True)
overlapp_trigular_points = np.array(list(spread_point_count.values())) > 3
overlapp_trigular_points_index = np.arange(len(overlapp_trigular_points))[overlapp_trigular_points]
ApplySmoothEffort(current_obj, overlapp_trigular_points_index, 0.5, 30)
ApplySmoothEffort(current_obj, factor=0.5, iterations=30)
bpy.ops.wm.obj_export(filepath=output_mesh_path)
def process_meshes(input_path, output_path):
"""
Processes each mesh file in the input directory and saves the cleaned meshes in the output directory.
Args:
input_path (str): Directory containing the input meshes or a single mesh file.
output_path (str): Directory where the cleaned meshes will be saved.
"""
if os.path.isdir(input_path):
for filename in sorted(os.listdir(input_path)):
full_input_path = os.path.join(input_path, filename)
full_output_path = os.path.join(output_path, filename)
process(full_input_path, full_output_path)
else:
process(input_path, output_path)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Processes mesh files.")
parser.add_argument("-ip", "--input_path", type=str, required=True, help="Input path for the mesh file or directory.")
parser.add_argument("-op", "--output_path", type=str, required=True, help="Output path for the cleaned mesh file or directory.")
args = parser.parse_args()
process_meshes(args.input_path, args.output_path)
+5
View File
@@ -0,0 +1,5 @@
open3d
numpy
sklearn
argparse
bpy
+78
View File
@@ -0,0 +1,78 @@
import os,sys,time,shlex,subprocess,shutil,requests,json
import platform
if platform.system() == 'Windows':
sys.path.append('e:\\libs\\')
#sys.path.append('libs')
else:
sys.path.append('/data/deploy/make3d/make2/libs/')
import libs,config
def main(pid = 0):
print(f'开始时间-{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}')
if pid == 0:
#获取要处理PID的url
url = "https://repair.api.suwa3d.com/api/modelRepairOrder/getRepairOrderListByPriority"
#发起请求
res = requests.get(url)
print('res:', res.text)
if res.status_code != 200:
print('获取失败,程序退出')
return
res = json.loads(res.text)
pid = 0
try:
pid = res['data']['pid']
except Exception as e:
return 0
if pid == 0:
return 0
#调用oss下载功能,下载对应的文件
pathStr = os.path.join(config.workdir,str(pid)+"_ai")
libs.down_obj_from_oss_auto(str(pid),"auto",pathStr)
#检查是否存在产生的对应的文件
if not os.path.exists(os.path.join(pathStr,f'{pid}.obj')):
print(f"找不到{pid}obj文件")
return
if not os.path.exists(os.path.join(pathStr,f'{pid}.mtl')):
print(f"找不到{pid}mtl文件")
return
#下载完成后调用
os.system(f"python optimize_model.py -ip {pathStr}/{pid}.obj -op {pathStr}/{pid}_ai.obj")
#检查是否存在产生的对应的文件
if not os.path.exists(os.path.join(pathStr,f'{pid}_ai.obj')):
print(f"找不到{pid}ai_obj文件")
return
if not os.path.exists(os.path.join(pathStr,f'{pid}_ai.mtl')):
print(f"找不到{pid}ai_mtl文件")
return
#处理完成完后上传到oss指定目录
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/fix_mesh/{pid}_ai.obj', os.path.join(pathStr,f'{pid}_ai.obj'))
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/fix_mesh/{pid}_ai.mtl', os.path.join(pathStr,f'{pid}_ai.mtl'))
config.oss_bucket.put_object_from_file(f'objs/auto/{pid}/ai/fix_mesh/{pid}.jpg', os.path.join(pathStr,f'{pid}.jpg'))
#更新处理结果
url = "https://repair.api.suwa3d.com/api/modelRepairOrder/updateShootOrderBuildModelType?pid="+str(pid)
#发起请求
requests.get(url)
#移除文件夹
shutil.rmtree(pathStr)
print(f'结束时间-{time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())}')
if __name__ == '__main__':
if len(sys.argv) == 2:
pid = sys.argv[1]
main(pid)
print(f"{pid}-处理结束")
else:
while True:
res = main()
if res == 0:
print("休眠60s")
time.sleep(60)
+22
View File
@@ -0,0 +1,22 @@
import paramiko
try:
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在known_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
print('start connect')
# 连接服务器
ssh.connect('connect.bjc1.seetacloud.com', username='root', password='QoQA8q3Ds2VB', port=33733)
print('connect success')
# 使用nohup执行命令,并将输出重定向到文件
command = 'nohup seg_python /data/code/optimize_model_xj/optimize_model.py -pid 189813 > output.log 2>&1 &'
stdin, stdout, stderr = ssh.exec_command(command)
# 打印命令是否启动成功
print("Command executed")
finally:
# 确保连接被关闭
if ssh:
ssh.close()
+58 -11
View File
@@ -1,10 +1,10 @@
import redis, os, sys,requests,platform
import redis, os, sys,requests,platform,json
if platform.system() == 'Windows':
sys.path.append('e:\\libs\\')
sys.path.append('E:\\make2\\libs\\')
#sys.path.append('libs')
else:
sys.path.append('/data/deploy/make3d/make2/libs/')
import config, libs,libs_db
import config, libs,libs_db,libs_db_task_rc
def main(cmd, order_id):
if cmd == 'print': #order_id
@@ -19,17 +19,45 @@ def main(cmd, order_id):
key = 'model:foot'
elif cmd == 'rebuild': #pid
key = 'model:rebuild'
elif cmd == 'gs':
key = 'ai:ai_build'
if order_id == 'view':
for i in r.lrange(key, 0, -1):
print(i)
print(f'当前{key}队列长度:{r.llen(key)}')
if cmd == "rc_model_build":
nums = libs_db_task_rc.get_task_rc_count()
print(f'当前{cmd}队列长度:{nums}')
notify(f'当前{cmd}队列长度:{nums}')
else:
print(f'当前{cmd}队列长度:{r.llen(key)}')
notify(f'当前{cmd}队列长度:{r.llen(key)}')
for i in r.lrange(key, 0, -1):
print(i)
print(f'当前{key}队列长度:{r.llen(key)}')
else:
order_ids = order_id.split(',')
for order_id in order_ids:
r.lpush(key, order_id)
print(f'已推送{order_id}到{key}, 当前队列长度:{r.llen(key)}')
def notify(content):
if content == "":
return "content 不能为空"
for user_agent_id in config.notify_user_Ids:
data = {
'userId': user_agent_id,
'message': content,
}
headers = {'Content-Type': 'application/json'}
message_send_url = "https://mp.api.suwa3d.com/api/qyNotify/sendMessage?userId="+user_agent_id+"&message="+content
response = requests.post(message_send_url, data=json.dumps(data), headers=headers)
def cmd(cmdName,pid):
if pid == "view":
key = ""
@@ -43,10 +71,18 @@ def cmd(cmdName,pid):
key = 'model:make10'
elif cmdName == 'foot': # print_id
key = 'model:foot'
for i in r.lrange(key, 0, -1):
print(i)
print(f'当前{key}队列长度:{r.llen(key)}')
exit()
if pid == 'view':
# for i in r.lrange(key, 0, -1):
# print(i)
print(f'当前{cmd}队列长度:{r.llen(key)}')
notify(f'当前{cmd}队列长度:{r.llen(key)}')
exit()
else:
for i in r.lrange(key, 0, -1):
print(i)
print(f'当前{key}队列长度:{r.llen(key)}')
exit()
res = requests.get(f'https://mp.api.suwa3d.com/api/infoQuery/infoByPid?pid={pid}&cmd={cmdName}')
#获取res 的数据
@@ -135,13 +171,24 @@ def listDataToStr(data,cmdName):
if __name__ == '__main__':
r = config.redis_remote
if len(sys.argv) == 3:
cmdName = sys.argv[1]
pid = sys.argv[2]
elif len(sys.argv) == 2:
cmd = sys.argv[1]
order_id = 'view'
if cmd == "auto_view":
arrCmd = ["print","foot","gs","rc_model_build"]
for cmdV in arrCmd:
main(cmdV, order_id)
exit(1)
else:
print('用法:python push_cmd.py <cmd> <order_id>')
exit(1)
r = config.redis_remote
#pid 可能是多个的,用逗号分隔,查询的时候接口应该也要支持多个的
if cmdName == 'make3d' or cmdName == 'make3d10' or cmdName == 'rebuild':