新的信息
This commit is contained in:
1 parent
c66fc76e10
commit
345790cb05
51 files changed
+5187
-61
No files matched your search
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,5 @@
|
||||
open3d
|
||||
numpy
|
||||
sklearn
|
||||
argparse
|
||||
bpy
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user