Browse Source

更新

main
hesuicong 2 weeks ago
parent
commit
84ff112176
  1. BIN
      __pycache__/compute_print_net.cpython-312.pyc
  2. BIN
      __pycache__/config.cpython-312.pyc
  3. BIN
      __pycache__/download_print.cpython-312.pyc
  4. BIN
      __pycache__/general.cpython-312.pyc
  5. BIN
      __pycache__/get_lowest_position_of_center_ext.cpython-312.pyc
  6. BIN
      __pycache__/get_lowest_position_of_z_out.cpython-312.pyc
  7. BIN
      __pycache__/grid_near_three.cpython-312.pyc
  8. BIN
      __pycache__/point_cloud_layout.cpython-312.pyc
  9. BIN
      __pycache__/print_factory_type_setting_obj_run.cpython-312.pyc
  10. BIN
      __pycache__/print_merged_many_obj.cpython-312.pyc
  11. BIN
      __pycache__/print_mplot3d_point_cloud_layout.cpython-312.pyc
  12. BIN
      __pycache__/print_show_weight_max_obj.cpython-312.pyc
  13. BIN
      __pycache__/test_load_json.cpython-312.pyc
  14. 20
      clound_print.py
  15. 748
      compute_print_net.py
  16. 18
      config.py
  17. 70
      download_print.py
  18. 112
      download_print_out.py
  19. 286
      general.py
  20. 847
      get_lowest_position_of_center_ext.py
  21. 352
      get_lowest_position_of_z_out.py
  22. 179
      grid_near_three.py
  23. 295
      output.log
  24. 1567
      point_cloud_layout.py
  25. 174
      print_factory_type_setting_obj_run.py
  26. 193
      print_factory_type_setting_obj_run_GUI.py
  27. 162
      print_merged_many_obj.py
  28. 3619
      print_mplot3d_point_cloud_layout.py
  29. 127
      print_setting_run.py
  30. 51
      print_setting_ui.py
  31. 314
      print_show_weight_max_obj.py
  32. 346
      print_type_setting_gui.py
  33. 38
      print_type_setting_gui.spec
  34. 126
      print_type_setting_gui_multi.py
  35. 25
      qt5_demo.py
  36. 44
      qt5_demo.spec
  37. 275
      sui_01.py
  38. 19
      test.py
  39. 34
      test_load_json.py
  40. 52
      x_y_min_test.py
  41. 152
      读写时间测试.py
  42. 98
      读写时间测试2.py

BIN
__pycache__/compute_print_net.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/config.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/download_print.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/general.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/get_lowest_position_of_center_ext.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/get_lowest_position_of_z_out.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/grid_near_three.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/point_cloud_layout.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/print_factory_type_setting_obj_run.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/print_merged_many_obj.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/print_mplot3d_point_cloud_layout.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/print_show_weight_max_obj.cpython-312.pyc

Binary file not shown.

BIN
__pycache__/test_load_json.cpython-312.pyc

Binary file not shown.

20
clound_print.py

File diff suppressed because one or more lines are too long

748
compute_print_net.py

@ -0,0 +1,748 @@ @@ -0,0 +1,748 @@
import open3d as o3d
import numpy as np
import copy
import time
import argparse
from general import *
# -------------------------- 开始:获取z值最低 --------------------------
"""
对外部提供的获取最低z的接口
get_lowest_position_of_z_out
参数:
obj_path, 模型数据路径
返回:
total_matrix: 旋转矩阵
z_max: Z最高点
"""
def get_lowest_position_of_z_out(mesh_obj):
total_matrix = np.eye(4)
voxel_size = 3
# print(f"obj_path={obj_path}, get_lowest_position_of_center voxel_size={voxel_size}")
start_time1 = time.time()
vertices = np.asarray(mesh_obj.vertices)
# 确保网格有顶点
if len(vertices) == 0:
# raise ValueError(f"Mesh has no vertices: {obj_path}")
print(f"Warning: Mesh has no vertices: {mesh_obj}")
return None
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# print("voxel_size",voxel_size,obj_path, len(pcd.points), len(mesh_obj.vertices))
# 对点云进行下采样(体素网格法)
#"""
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
if len(np.asarray(pcd_downsampled.points)) <= 0:
bbox = pcd.get_axis_aligned_bounding_box()
volume = bbox.volume()
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}")
# 处理体积为零的情况
if volume <= 0:
# 计算点云的实际范围
points = np.asarray(pcd.points)
if len(points) > 0:
min_bound = np.min(points, axis=0)
max_bound = np.max(points, axis=0)
extent = max_bound - min_bound
# 确保最小维度至少为0.01
min_dimension = max(0.01, np.min(extent))
volume = min_dimension ** 3
else:
volume = 1.0 # 最后的安全回退
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}")
# 安全计算密度 - 防止除零错误
if len(pcd.points) > 0 and volume > 0:
original_density = len(pcd.points) / volume
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33)))
else:
# 当点数为0或体积为0时使用默认体素大小
voxel_size = 1.0 # 默认值
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}")
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
original_num = len(pcd.points)
target_samples = 1000
num_samples = min(target_samples, original_num)
# print("get_lowest_position_of_center1 time", time.time()-start_time1)
start_time2 = time.time()
# 确保下采样后有点云
if len(np.asarray(pcd_downsampled.points)) == 0:
# 使用原始点云作为后备
pcd_downsampled = pcd
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points")
points = np.asarray(pcd_downsampled.points)
# 初始化最小重心Y的值
max_z_of_mass_y = float('inf')
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
best_angle_x, best_angle_y, best_angle_z, max_z_of_mass_y = parallel_rotation(points, angle_step=3)
# 使用最佳角度进行旋转并平移obj
pcd_transformed = copy.deepcopy(mesh_obj)
# 最佳角度旋转
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x))
pcd_transformed.rotate(R_x)
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y))
pcd_transformed.rotate(R_y)
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z))
pcd_transformed.rotate(R_z)
T_x = np.eye(4)
T_x[:3, :3] = R_x
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_x @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
T_y = np.eye(4)
T_y[:3, :3] = R_y
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_y @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
T_z = np.eye(4)
T_z[:3, :3] = R_z
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_z @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
#试着旋转180,让脸朝上
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
T_trans1 = np.eye(4)
T_trans1[:3, 3] = translation_vector
total_matrix = T_trans1 @ total_matrix
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean1 = np.mean(vertices[:, 2])
z_max1 = np.max(vertices[:, 2])
start_time_v1 = time.time()
volume_centroid = get_volume_centroid(pcd_transformed)
z_volume_center1 = volume_centroid[2]
delta = time.time() - start_time_v1
# print(f"get_volume_centroid time={delta}")
angle_rad = np.pi
#print("旋转前质心:", pcd_transformed.get_center())
#print("旋转前点示例:", np.asarray(pcd_transformed.vertices)[:3])
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
aabb = pcd_transformed.get_axis_aligned_bounding_box()
# center_point = aabb.get_center()
center_point = compute_mesh_center(mesh_obj.vertices)
# 构建绕中心点旋转的变换矩阵[3](@ref)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
R_y180 = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
T_rotate = np.eye(4)
T_rotate[:3, :3] = R_y180
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_rotate @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
#print("旋转后质心:", pcd_transformed.get_center())
#print("旋转后点示例:", np.asarray(pcd_transformed.vertices)[:3])
#
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
max_z = np.max(vertices[:, 2])
# print("min_z1", min_z, obj_path)
translation_vector = np.array([0,0,-min_z,])
# translation_vector = np.array([0,0,-min_z + (min_z-max_z),])
# print("translation_vector1",translation_vector)
pcd_transformed.translate(translation_vector)
T_trans2 = np.eye(4)
T_trans2[:3, 3] = translation_vector
translation = total_matrix[:3, 3]
# print("translation_vector2",translation_vector)
# print(1,translation)
total_matrix = T_trans2 @ total_matrix
translation = total_matrix[:3, 3]
# print(2,translation)
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean2 = np.mean(vertices[:, 2])
z_max2 = np.max(vertices[:, 2])
volume_centroid = get_volume_centroid(pcd_transformed)
z_volume_center2 = volume_centroid[2]
# print(f"get_lowest_position_of_center z_max1={z_max1}, z_max2={z_max2}, len={len(pcd_transformed.vertices)}, obj_path={obj_path}")
# print(f"z_mean1={z_mean1}, z_mean2={z_mean2}")
# if (z_mean2 > z_mean1):
print(f"z_volume_center1={z_volume_center1}, z_volume_center2={z_volume_center2}")
if (z_volume_center2 > z_volume_center1):
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
centroid = pcd_transformed.get_center()
aabb = pcd_transformed.get_axis_aligned_bounding_box()
# center_point = aabb.get_center()
center_point = compute_mesh_center(mesh_obj.vertices)
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
# 构建反向旋转矩阵
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
T_rotate_inv = np.eye(4)
T_rotate_inv[:3, :3] = R_y
# 完整的反向绕中心旋转矩阵
T_rot_center_inv = T_origin_to_center @ T_rotate_inv @ T_center_to_origin
total_matrix = T_rot_center_inv @ total_matrix
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
# print("min_z2", min_z, obj_path)
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
T_trans3 = np.eye(4)
T_trans3[:3, 3] = translation_vector
total_matrix = T_trans3 @ total_matrix
# z_mean_min = min(z_mean1, z_mean2)
z_max = min(z_max1, z_max2)
# print("get_lowest_position_of_center2 time", time.time()-start_time2)
return total_matrix, z_max
def get_lowest_position_of_center_ext(mesh_obj, total_matrix):
# print(f"get_lowest_position_of_center_ext {obj_path}")
temp_matrix, z_max = get_lowest_position_of_z_out(mesh_obj)
total_matrix = temp_matrix @ total_matrix
return total_matrix, z_max
def calculate_rotation_and_top_of_mass(angle_x, angle_y, angle_z, points):
"""计算某一组旋转角度后的重心"""
# 计算绕X轴、Y轴和Z轴的旋转矩阵
R_x = np.array([
[1, 0, 0],
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
])
R_y = np.array([
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
[0, 1, 0],
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
])
R_z = np.array([
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
[0, 0, 1]
])
# 综合旋转矩阵
R = R_z @ R_y @ R_x
# 执行旋转
rotated_points = points @ R.T
# 计算最小z值
min_z = np.min(rotated_points[:, 2])
# 计算平移向量,将最小Z值平移到0
translation_vector = np.array([0, 0, -min_z])
rotated_points += translation_vector
top_of_mass = np.max(rotated_points, axis=0)
return top_of_mass[2], angle_x, angle_y, angle_z
def parallel_rotation(points, angle_step=4):
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)"""
max_top = float('inf')
for angle_x in range(-90, 90, angle_step):
for angle_y in range(0, 360, angle_step):
max_z, ax, ay, _ = calculate_rotation_and_top_of_mass(angle_x, angle_y, 0, points)
if max_z < max_top:
max_top = max_z
best_angle_x = ax
best_angle_y = ay
return (best_angle_x, best_angle_y, 0, max_top)
def compute_mesh_center(vertices):
if len(vertices) == 0:
raise ValueError("顶点数组不能为空")
# 确保vertices是NumPy数组
vertices_np = np.asarray(vertices)
# 使用NumPy的mean函数直接计算均值(向量化操作)
centroid = np.mean(vertices_np, axis=0)
return centroid
# -------------------------- 结束:获取z值最低 --------------------------
# -------------------------- 开始:bbox --------------------------
def get_models_bbox(dict_pcd_fix):
"""
单独提取从dict_fix中解析所有模型的包围盒bbox尺寸信息
:param dict_fix: 包含PLY文件名和对应点云的字典
:return: 模型列表包含name和dimensions
"""
all_models = []
extend_dist = 2 # 尺寸扩展量(单位:厘米)
for ply_file in dict_pcd_fix:
# 解析PLY文件名中的尺寸信息(格式:"模型ID=维度1+维度2+维度3.ply")
bbox_with_text = ply_file.split("=")
bbox_with = bbox_with_text[-1]
split_text = bbox_with.replace(".ply", "").split("+")
# 转换单位:米 → 厘米 → 加扩展量 → 转回米(int取整避免浮点数精度问题)
x_length = int(float(split_text[2]) * 100) + extend_dist # 第三个维度→x方向
y_length = int(float(split_text[0]) * 100) + extend_dist # 第一个维度→y方向
z_length = int(float(split_text[1]) * 100) + extend_dist # 第二个维度→z方向
all_models.append({
'name': ply_file,
'dimensions': (int(x_length / 100), int(z_length / 100), int(y_length / 100)) # 单位:米
})
return all_models
def arrange_models_on_platform(models, machine_size):
"""
单独提取将模型在打印平台上进行排版布局
:param models: 由get_models_bbox返回的模型列表包含name和dimensions
:param machine_size: 打印机尺寸 (width, depth, height)
:return: (placed_models, unplaced_models) - 已放置和未放置的模型列表
"""
# 初始化打印平台
platform = Platform(
int(machine_size[0]),
int(machine_size[1]),
int(machine_size[2])
)
print("开始计算排序...")
platform.arrange_models(models)
platform.print_results()
return platform.get_result()
import os
def compute_bbox_all_ext(base_original_obj_dir,compact_min_dis=True):
obj_id_list = [aa.split(".o")[0] for aa in os.listdir(base_original_obj_dir) if aa.endswith(".obj")]
obj_id_list = obj_id_list
dict_mesh_obj = {}
for pid_t_y in obj_id_list:
obj_name = pid_t_y+".obj"
obj_path = os.path.join(base_original_obj_dir,obj_name)
mesh_obj = read_mesh(obj_path)
if mesh_obj is None:
continue
dict_mesh_obj[obj_name] = mesh_obj
return compute_bbox_all(dict_mesh_obj,compact_min_dis)
def compute_bbox_all(dict_mesh_obj,compact_min_dis):
dict_total_matrix= {}
dict_pcd_fix= {}
for key, value in dict_mesh_obj.items():
start_time = time.time()
obj_name = key
mesh_obj = value
total_matrix, pcd_fix, ply_name = compute_bbox(mesh_obj,obj_name,compact_min_dis)
dict_total_matrix[obj_name] = total_matrix
dict_pcd_fix[ply_name] = pcd_fix
print(f"compute_bbox {obj_name} time={time.time()-start_time}")
all_models = get_models_bbox(dict_pcd_fix)
return dict_total_matrix,all_models
def compute_bbox(mesh_obj,obj_name,compact_min_dis):
total_matrix = np.eye(4)
total_matrix, z_min= get_lowest_position_of_center_ext(mesh_obj, total_matrix)
transformed_vertices = mesh_transform_by_matrix(np.asarray(mesh_obj.vertices), total_matrix)
obj_transformed = copy.deepcopy(mesh_obj)
obj_transformed.vertices = o3d.utility.Vector3dVector(transformed_vertices)
voxel_size = 3 # 设置体素的大小,决定下采样的密度
# 将点云摆正和X轴平衡
obj_transformed_second,total_matrix = arrange_box_correctly(obj_transformed,voxel_size,total_matrix)
obj_transformed,total_matrix,pcd_fix,ply_name = get_new_bbox(obj_transformed_second,obj_name,voxel_size,compact_min_dis,total_matrix)
return total_matrix,pcd_fix,ply_name
def arrange_box_correctly(obj_transformed, voxel_size,total_matrix):
vertices = np.asarray(obj_transformed.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# 降采样与特征计算
pcd_downsampled = down_sample(pcd, voxel_size)
points = np.asarray(pcd_downsampled.points)
cov = np.cov(points.T)
center = obj_transformed.get_center()
# 特征分解与方向约束(关键修改点)
eigen_vals, eigen_vecs = np.linalg.eigh(cov)
max_axis = eigen_vecs[:, np.argmax(eigen_vals)]
# print("max_axis", max_axis)
# 强制主方向向量X分量为正(指向右侧)
if max_axis[0] < 0 or (max_axis[0] == 0 and max_axis[1] < 0):
max_axis = -max_axis
target_dir = np.array([1, 0]) # 目标方向为X正轴
current_dir = max_axis[:2] / np.linalg.norm(max_axis[:2])
dot_product = np.dot(current_dir, target_dir)
# print("dot_product", dot_product)
if dot_product < 0.8: # 阈值控制方向敏感性(建议0.6~0.9)
max_axis = -max_axis # 强制翻转方向
# 计算旋转角度
angle_z = np.arctan2(max_axis[1], max_axis[0]) % (2 * np.pi)
if max_axis[0] <= 0 and max_axis[1] <= 0:
angle_z += np.pi
R = o3d.geometry.get_rotation_matrix_from_axis_angle([0, 0, -angle_z])
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = center - R.dot(center) # 保持中心不变
obj_transformed.transform(T)
total_matrix = T @ total_matrix
return obj_transformed, total_matrix
def get_new_bbox(obj_transformed_second,obj_name,voxel_size,compact_min_dis,total_matrix):
# 计算点云的边界
points = np.asarray(obj_transformed_second.vertices)
min_bound = np.min(points, axis=0) # 获取点云的最小边界
max_bound = np.max(points, axis=0) # 获取点云的最大边界
# 确保包围盒的Y坐标不低于0
min_bound[2] = max(min_bound[2], 0) # 确保Y坐标的最小值不低于0
# 重新计算包围盒的中心和半长轴
bbox_center = (min_bound + max_bound) / 2 # 计算包围盒的中心点
bbox_extent = (max_bound - min_bound) # 计算包围盒的半长轴(尺寸)
# 创建包围盒,确保尺寸正确
new_bbox = o3d.geometry.OrientedBoundingBox(center=bbox_center,
R=np.eye(3), # 旋转矩阵,默认没有旋转
extent=bbox_extent)
# 获取包围盒的长、宽和高
x_length = round(bbox_extent[0],3) # X 方向的长
y_length = round(bbox_extent[1],3) # Y 方向的宽
z_length = round(bbox_extent[2],3) # Z 方向的高
bbox_points = np.array([
[min_bound[0], min_bound[1], min_bound[2]],
[max_bound[0], min_bound[1], min_bound[2]],
[max_bound[0], max_bound[1], min_bound[2]],
[min_bound[0], max_bound[1], min_bound[2]],
[min_bound[0], min_bound[1], max_bound[2]],
[max_bound[0], min_bound[1], max_bound[2]],
[max_bound[0], max_bound[1], max_bound[2]],
[min_bound[0], max_bound[1], max_bound[2]]
])
first_corner = bbox_points[2]
translation_vector = -first_corner
obj_transformed_second.translate(translation_vector)
T_trans = np.eye(4)
T_trans[:3, 3] = translation_vector # 设置平移分量 [2,3](@ref)
total_matrix = T_trans @ total_matrix # 矩阵乘法顺序:最新变换左乘[4,5](@ref)
new_bbox.translate(translation_vector)
vertices = np.asarray(obj_transformed_second.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
if compact_min_dis:
pcd_downsampled = down_sample(pcd, voxel_size, False)
pcd_fix = pcd_downsampled
else:
pcd_fix = pcd
ply_print_pid = obj_name.replace(".obj","")
ply_name = f"{ply_print_pid}={z_length}+{y_length}+{x_length}.ply"
return obj_transformed_second, total_matrix, pcd_fix, ply_name
class Platform:
def __init__(self, width, depth, height):
self.width = width
self.depth = depth
self.height = height
self.placed_models = [] # 已放置的模型
self.unplaced_models = [] # 未能放置的模型
self.first_line = True
self.remove_multiobj_name = ""
def is_cross_border(self, x, y, z, model):
mx, my, mz = model['dimensions']
return is_cross_border_c(x, y, z, mx, my, mz, self.width, self.depth, self.height)
def check_multiobj_cross_pre(self, name, pre_model):
if (pre_model==None):
return
if not is_same_obj(name, pre_model['name']):
return
self.unplaced_models.append(pre_model)
if pre_model in self.placed_models:
self.placed_models.remove(pre_model)
if "pre_model" in pre_model:
self.pre_model = pre_model["pre_model"]
self.check_multiobj_cross_pre(name, self.pre_model)
def check_multiobj_cross(self, model):
if not is_multi_obj(model['name']):
return False
self.unplaced_models.append(model)
if "pre_model" in model:
self.pre_model = model["pre_model"]
self.check_multiobj_cross_pre(model['name'], self.pre_model)
return True
def can_place(self, x, y, z, model, is_print=False):
mx, my, mz = model['dimensions']
if self.is_cross_border(x, y, z, model):
print(f"can_place False 1 cross_border {x}, {y}, {z}, {model}, {self.width}, {self.depth}, {self.height}")
return False
# 碰撞检测(正确逻辑与间距处理)
for placed in self.placed_models:
px, py, pz = placed['position']
pdx, pdy, pdz = placed['dimensions']
# 使用AABB碰撞检测算法[4](@ref)
if (x > px - pdx - extend_dist_model_x and
x - mx - extend_dist_model_x < px and
y > py - pdy - extend_dist_model_y and
y - my - extend_dist_model_y < py and
z < pz + pdz and
z + mz > pz):
print("can_place False 2",False,model,x,y,z,px,pdx,extend_dist_model_x,py,pdy,extend_dist_model_y,my,pz,pdz,pz)
return False
return True
def place_model(self, model, pre_model):
mx, my, mz = model['dimensions']
if mz > self.height:
self.unplaced_models.append(model)
return False
if is_same_obj(model['name'], self.remove_multiobj_name):
self.unplaced_models.append(model)
return False
z = 0
if pre_model is None:
if self.first_line:
model['position'] = (mx + extend_dist_border_x_min, self.depth - extend_dist_border_y_max, 0)
print(f"First Model {model['name']}")
model['first_line'] = True
else:
model['position'] = (self.width - extend_dist_border_x_max, self.depth - extend_dist_border_y_max, 0)
model['first_line'] = False
print("model position1", model['name'], model['position'])
self.placed_models.append(model)
return True
pre_px, pre_py, pre_pz = pre_model['position']
pre_mx, pre_my, pre_mz = pre_model['dimensions']
if self.first_line:
px = pre_px + mx + extend_dist_model_x
model['first_line'] = True
else:
px = pre_px - pre_mx - extend_dist_model_x
model['first_line'] = False
print(model['name'], "px", px, pre_px, pre_mx)
reach_limit_x = False
if self.first_line:
if px > self.width:
reach_limit_x = True
else:
if px - mx < 0:
reach_limit_x = True
if reach_limit_x:
self.first_line = False
px = self.width - extend_dist_border_x_max
start_y = my + extend_dist_border_y_min
final_y = self.depth
print("reach_limit_x final_y1", model['name'], my, final_y, my, extend_dist_border_x_max, px)
for y in range(start_y, final_y, +1):
# print("y",y)
if self.can_place(px, y, z, model, True)==False:
y -= 1
if self.is_cross_border(px, y, z, model):
print(f"cross border : {model['name']}")
if self.check_multiobj_cross(model):
self.remove_multiobj_name = model['name']
return False
model['position'] = (px, y, z)
print("model position2", model['name'], model['position'])
self.placed_models.append(model)
return True
else:
start_y = my + extend_dist_border_y_min
final_y = self.depth
print("final_y2", model['name'], start_y, final_y, my, extend_dist_border_y_max, px)
for y in range(start_y, final_y, +1):
if self.can_place(px, y, z, model)==False:
y -= 1
if self.is_cross_border(px, y, z, model):
print(f"cross border : {model['name']}")
if self.check_multiobj_cross(model):
self.remove_multiobj_name = model['name']
return False
model['position'] = (px, y, z)
print("model position2", model['name'], model['position'])
self.placed_models.append(model)
return True
if 'position' in model:
print("model position3", model['name'], model['position'])
else:
print("model position3 no exist position", model['name'])
self.unplaced_models.append(model)
return False
def arrange_models(self, models):
"""对所有模型进行排布(单层)"""
print(" 单层放置模式:所有模型只能放在平台底面(Z=0)")
# 按高度和面积排序,优先放大模型
models = sorted(models, key=lambda m: (-m['dimensions'][2], -m['dimensions'][0] * m['dimensions'][1]))
self.pre_model = None
for model in models:
print(f"arrange_models {model['name']}")
pre_model_temp = self.pre_model
if self.place_model(model, self.pre_model):
self.pre_model = model
model["pre_model"] = pre_model_temp
def print_results(self):
"""打印排布结果"""
print("Placed Models:")
for model in self.placed_models:
print(f" - {model['name']} at {model['position']} with dimensions {model['dimensions']}")
print("Unplaced Models:")
for model in self.unplaced_models:
print(f" - {model['name']} with dimensions {model['dimensions']}")
def get_result(self):
return self.placed_models, self.unplaced_models
# -------------------------- 结束:bbox --------------------------
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--obj_path", type=str, required=True, help="batchobj_path_id")
args = parser.parse_args()
obj_path = args.obj_path
max, z = get_lowest_position_of_z_out(obj_path)

18
config.py

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
from enum import Enum
class run_mode(Enum):
formal = 1
test = 2
fix = 3
# !!!谨慎选择run_mode.fix,这将把修复的数据上传到正式的系统 !!!
curr_run_mode = run_mode.test
is_test = True
# print_factory_type_dir="/root/print_factory_type"
print_factory_type_dir="/home/algo/Documents/print_factory_type"
url_send_layout = 'https://mp.api.suwa3d.com/api/printTypeSettingOrder/printTypeSettingOrderSuccess'
big_machine_size = [600, 500, 300]
small_machine_size = [380, 345, 250]

70
download_print.py

@ -13,7 +13,10 @@ import math @@ -13,7 +13,10 @@ import math
import os
import argparse
is_test = False
from config import print_factory_type_dir
from config import is_test
from general import is_use_debug_oss
CameraModel = collections.namedtuple(
"CameraModel", ["model_id", "model_name", "num_params"]
@ -1399,12 +1402,8 @@ def download_transform_save_by_batch(batch_id, workdir, oss_config): @@ -1399,12 +1402,8 @@ def download_transform_save_by_batch(batch_id, workdir, oss_config):
layout_data = datas["layout_data"]
original_obj_pid_dir = workdir
cache_type_setting_dir = os.path.join(workdir, "arrange")
Path(cache_type_setting_dir).mkdir(exist_ok=True)
print(f"original_obj_pid_dir={original_obj_pid_dir}, cache_type_setting_dir={cache_type_setting_dir}")
transform_save(layout_data, original_obj_pid_dir, cache_type_setting_dir)
transform_save_o3d(layout_data, original_obj_pid_dir)
def download_datas_by_json(pid_file, workdir, oss_config):
@ -1427,12 +1426,8 @@ def download_transform_save_by_json(pid_file, workdir, oss_config): @@ -1427,12 +1426,8 @@ def download_transform_save_by_json(pid_file, workdir, oss_config):
layout_data = download_datas_by_json(pid_file, workdir, oss_config)
original_obj_pid_dir = workdir
cache_type_setting_dir = os.path.join(workdir, "arrange")
Path(cache_type_setting_dir).mkdir(exist_ok=True)
print(f"original_obj_pid_dir={original_obj_pid_dir}, cache_type_setting_dir={cache_type_setting_dir}")
transform_save(layout_data, original_obj_pid_dir, cache_type_setting_dir)
transform_save_o3d(layout_data, original_obj_pid_dir)
def upload_result(base_original_obj_dir, oss_config, batch_id):
@ -1442,7 +1437,9 @@ def upload_result(base_original_obj_dir, oss_config, batch_id): @@ -1442,7 +1437,9 @@ def upload_result(base_original_obj_dir, oss_config, batch_id):
target_dir = f"{base_original_obj_dir}"
oss_batch_dir = "batchPrint"
if is_test:
print(f"is_use_debug_oss={is_use_debug_oss()}")
if is_use_debug_oss():
# if is_test:
oss_batch_dir = "batchPrint/debug_hsc"
print(f"target_dir={target_dir}, batch_id={batch_id}")
@ -1456,52 +1453,6 @@ def upload_result(base_original_obj_dir, oss_config, batch_id): @@ -1456,52 +1453,6 @@ def upload_result(base_original_obj_dir, oss_config, batch_id):
pass
import open3d as o3d
from test_load_json import custom_mesh_transform
def transform_save(layout_data, original_obj_pid_dir, cache_type_setting_dir):
meshes = []
# 小打印机380*345,需要偏移-380,-345
need_offset = True
for model in layout_data["models"]:
transform = model.get('transform', {})
homo_matrix = transform["homo_matrix"] # 获取存储的列表
reconstructed_matrix = np.array(homo_matrix, dtype=np.float64)
obj_name = model.get('file_name', '')
obj_path = os.path.join(original_obj_pid_dir, obj_name)
# 加载网格
try:
mesh = o3d.io.read_triangle_mesh(obj_path, enable_post_processing=True)
if not mesh.has_vertices():
print(f"警告: 网格无有效顶点 - {obj_path}")
continue
except Exception as e:
print(f"加载模型失败: {obj_path} - {e}")
continue
original_vertices = np.asarray(mesh.vertices)
transformed_vertices = custom_mesh_transform(original_vertices, reconstructed_matrix)
# 如果 need_offset 为 True,应用额外的偏移
if need_offset:
# 应用偏移 (-380, -345, 0)
offset = np.array([-380, -345, 0])
transformed_vertices += offset
print(f"已对模型 {obj_name} 应用偏移: {offset}")
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices)
meshes.append(mesh)
# obj_path_arrange = os.path.join(original_obj_pid_dir, "arrange")
obj_path_arrange = cache_type_setting_dir
if not os.path.exists(obj_path_arrange):
os.mkdir(obj_path_arrange)
obj_path_arrange_obj = os.path.join(obj_path_arrange, obj_name)
print("obj_path_arrange_obj", obj_path_arrange_obj)
mesh.compute_vertex_normals()
o3d.io.write_triangle_mesh(obj_path_arrange_obj, mesh,write_triangle_uvs=True)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
@ -1517,9 +1468,8 @@ if __name__ == "__main__": @@ -1517,9 +1468,8 @@ if __name__ == "__main__":
args = parser.parse_args()
"""
# batch_id = args.batch_id
batch_id = 2499
batch_id = 10118
print_factory_type_dir="/root/print_factory_type"
# workdir = args.workdir
workdir = f"{print_factory_type_dir}/{batch_id}"

112
download_print_out.py

@ -1,33 +1,22 @@ @@ -1,33 +1,22 @@
import yaml
import oss2
import os
from tqdm import tqdm
import os
from pathlib import Path
import numpy as np
import os
import argparse
import bpy
import sys
import open3d as o3d
def custom_mesh_transform(vertices, transform_matrix):
"""
手动实现网格变换对每个顶点应用齐次变换矩阵
参数:
vertices: 网格顶点数组 (N, 3)
transform_matrix: 4x4 齐次变换矩阵
返回:
变换后的顶点数组 (N, 3)
"""
# 1. 顶点转齐次坐标 (N, 3) → (N, 4)
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1))))
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN)
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T
import requests
import json
import shutil
# 3. 转回非齐次坐标 (3xN) → (N, 3)
transformed_vertices = transformed_homogeneous[:3, :].T
return transformed_vertices
def download_transform_save_by_json(pid_file, workdir, oss_config):
layout_data = download_datas_by_json(pid_file, workdir, oss_config)
original_obj_pid_dir = workdir
transform_save_bpy(layout_data, original_obj_pid_dir)
class DataTransfer:
'''
@ -43,10 +32,6 @@ class DataTransfer: @@ -43,10 +32,6 @@ class DataTransfer:
self.oss_path = oss_path.lstrip('/')
self.oss_client = oss_client
order_id: str
pid: str
model_height: str
def download_data_rename_json(self, json_model_info):
"""
OSS 下载数据到本地保持原有目录结构
@ -163,10 +148,6 @@ class DataTransfer: @@ -163,10 +148,6 @@ class DataTransfer:
print(f"下载文件: {obj_key} -> {local_path}")
import requests
import json
import shutil
def get_api(url):
try:
response = requests.get(url)
@ -185,6 +166,7 @@ class JSONModelInfo: @@ -185,6 +166,7 @@ class JSONModelInfo:
obj_name: str
order_id: str
pid: str
print_order_id: str
model_height: str
def read_pids_from_json(pid_file):
@ -217,15 +199,19 @@ def read_pids_from_json(pid_file): @@ -217,15 +199,19 @@ def read_pids_from_json(pid_file):
order_id = parts[0]
pid = parts[1]
print_order_id = parts[2]
print_order_id = print_order_id.replace("P", "")
model_height = parts[3]
model_info = JSONModelInfo(
obj_name=obj_name,
order_id=order_id,
pid=pid,
print_order_id=print_order_id,
model_height=model_height
)
list_model_info.append(model_info)
print(f"model_info={model_info}")
return list_model_info, data
@ -233,7 +219,6 @@ def download_data_by_json(model_info, workdir, oss_client ): @@ -233,7 +219,6 @@ def download_data_by_json(model_info, workdir, oss_client ):
try:
pid = model_info.pid
model_height = model_info.model_height
# target_dir = f"{workdir}/{pid}_image"
target_dir = f"{workdir}"
url = f"https://mp.api.suwa3d.com/api/order/getOssSuffixByOrderId?order_id={model_info.order_id}"
@ -252,7 +237,7 @@ def download_data_by_json(model_info, workdir, oss_client ): @@ -252,7 +237,7 @@ def download_data_by_json(model_info, workdir, oss_client ):
shutil.rmtree(target_dir)
print(f"下载后检查发现目标文件夹为空,已删除: {target_dir}")
except Exception as e:
print(f"卡通图片下载失败: {pid}, 错误: {str(e)}")
print(f"下载失败: {pid}, 错误: {str(e)}")
pass
def get_oss_client(cfg_path):
@ -286,69 +271,24 @@ def download_datas_by_json(pid_file, workdir, oss_config): @@ -286,69 +271,24 @@ def download_datas_by_json(pid_file, workdir, oss_config):
return data
def download_transform_save_by_json(pid_file, workdir, oss_config):
layout_data = download_datas_by_json(pid_file, workdir, oss_config)
original_obj_pid_dir = workdir
cache_type_setting_dir = os.path.join(workdir, "arrange")
Path(cache_type_setting_dir).mkdir(exist_ok=True)
print(f"original_obj_pid_dir={original_obj_pid_dir}, cache_type_setting_dir={cache_type_setting_dir}")
transform_save(layout_data, original_obj_pid_dir, cache_type_setting_dir)
def transform_save(layout_data, original_obj_pid_dir, cache_type_setting_dir):
meshes = []
# 小打印机380*345,需要偏移-380,-345
need_offset = True
for model in layout_data["models"]:
transform = model.get('transform', {})
if __name__ == "__main__":
homo_matrix = transform["homo_matrix"] # 获取存储的列表
reconstructed_matrix = np.array(homo_matrix, dtype=np.float64)
script_args = []
obj_name = model.get('file_name', '')
obj_path = os.path.join(original_obj_pid_dir, obj_name)
# 加载网格
try:
mesh = o3d.io.read_triangle_mesh(obj_path, enable_post_processing=True)
if not mesh.has_vertices():
print(f"警告: 网格无有效顶点 - {obj_path}")
continue
except Exception as e:
print(f"加载模型失败: {obj_path} - {e}")
continue
original_vertices = np.asarray(mesh.vertices)
transformed_vertices = custom_mesh_transform(original_vertices, reconstructed_matrix)
# 如果 need_offset 为 True,应用额外的偏移
if need_offset:
# 应用偏移 (-380, -345, 0)
offset = np.array([-380, -345, 0])
transformed_vertices += offset
print(f"已对模型 {obj_name} 应用偏移: {offset}")
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices)
meshes.append(mesh)
# obj_path_arrange = os.path.join(original_obj_pid_dir, "arrange")
obj_path_arrange = cache_type_setting_dir
if not os.path.exists(obj_path_arrange):
os.mkdir(obj_path_arrange)
obj_path_arrange_obj = os.path.join(obj_path_arrange, obj_name)
print("obj_path_arrange_obj", obj_path_arrange_obj)
mesh.compute_vertex_normals()
o3d.io.write_triangle_mesh(obj_path_arrange_obj, mesh,write_triangle_uvs=True)
separator_index = sys.argv.index("--")
script_args = sys.argv[separator_index + 1:]
except ValueError:
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--pid_file", type=str, required=True, help="批次号, 也是json文件名")
parser.add_argument("--workdir", type=str, required=True, help="本代码文件所在的目录")
parser.add_argument("--oss_config", type=str, required=True)
args = parser.parse_args()
parser.add_argument("--workdir", type=str, required=True, help="json文件所在的目录")
parser.add_argument("--oss_config", type=str, required=True, help="run.yaml所在的目录")
args = parser.parse_args(script_args)
download_transform_save_by_json(args.pid_file, args.workdir, args.oss_config)
# blender --background --python download_print_out.py -- --pid_file {your_batch_id} --workdir {your_batch_dir} --oss_config {your_yaml_dir/run.yaml}

286
general.py

@ -0,0 +1,286 @@ @@ -0,0 +1,286 @@
import open3d as o3d
import numpy as np
import re
from config import *
# -------------------------- 开始:运行 ----------------------------------
def is_run_local_data():
if curr_run_mode == run_mode.formal:
return False
return True
def need_upload_result():
if curr_run_mode == run_mode.test:
return False
return True
def is_use_debug_oss():
if curr_run_mode == run_mode.test:
return True
return False
# -------------------------- 结束:运行 ----------------------------------
# -------------------------- 开始:模型 ----------------------------------
def read_mesh(obj_path):
mesh_obj = o3d.io.read_triangle_mesh(obj_path)
return mesh_obj
def down_sample(pcd, voxel_size, farthest_sample = False):
original_num = len(pcd.points)
target_samples = 1500 # 1000
num_samples = min(target_samples, original_num)
# 第一步:使用体素下采样快速减少点数量
# voxel_size = 3
if farthest_sample:
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_voxel = pcd.voxel_down_sample(voxel_size)
down_num = len(pcd_voxel.points)
# print(f"original_num={original_num}, down_num={down_num}")
# 第二步:仅在必要时进行最远点下采样
if len(pcd_voxel.points) > target_samples and False:
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_downsampled = pcd_voxel
return pcd_downsampled
def get_volume_centroid(mesh):
"""向量化版本的体积重心计算"""
vertices = np.asarray(mesh.vertices)
triangles = np.asarray(mesh.triangles)
# 一次性获取所有三角形的顶点 [n_triangles, 3, 3]
tri_vertices = vertices[triangles]
# 分别提取三个顶点
v0 = tri_vertices[:, 0, :] # [n, 3]
v1 = tri_vertices[:, 1, :] # [n, 3]
v2 = tri_vertices[:, 2, :] # [n, 3]
# 向量化计算叉积和点积
cross_vec = np.cross(v1, v2) # [n, 3]
dot_results = np.einsum('ij,ij->i', v0, cross_vec) # 批量点积 [n]
# 计算每个四面体体积 [n]
tetra_volumes = np.abs(dot_results) / 6.0
# 计算每个四面体重心 [n, 3]
tetra_centers = (v0 + v1 + v2) / 4.0
# 总体积和加权重心
total_volume = np.sum(tetra_volumes)
if total_volume > 0:
weighted_center = np.sum(tetra_volumes[:, np.newaxis] * tetra_centers, axis=0) / total_volume
return weighted_center
else:
return np.mean(vertices, axis=0)
def mesh_tranform_to_pcd(mesh, transform_matrix):
vertices = np.asarray(mesh.vertices)
transformed_vertices = mesh_transform_by_matrix(vertices, transform_matrix)
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices)
return mesh_to_pcd(mesh)
def mesh_to_pcd(mesh, is_down_sample=True):
vertices = np.asarray(mesh.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
if is_down_sample:
pcd_downsampled = down_sample(pcd, voxel_size, False)
return pcd_downsampled
else:
return pcd
def mesh_transform_by_matrix(vertices, transform_matrix):
"""
手动实现网格变换对每个顶点应用齐次变换矩阵
参数:
vertices: 网格顶点数组 (N, 3)
transform_matrix: 4x4 齐次变换矩阵
返回:
变换后的顶点数组 (N, 3)
"""
# 1. 顶点转齐次坐标 (N, 3) → (N, 4)
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1))))
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN)
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T
# 3. 转回非齐次坐标 (3xN) → (N, 3)
transformed_vertices = transformed_homogeneous[:3, :].T
return transformed_vertices
import os
def transform_save_bpy(layout_data, original_obj_pid_dir):
# 清除场景
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
## 尺寸调整,环境设置
bpy.ops.object.delete(use_global=False, confirm=False)
bpy.context.scene.unit_settings.length_unit = 'CENTIMETERS'
bpy.context.scene.unit_settings.scale_length = 0.001
bpy.context.scene.unit_settings.mass_unit = 'GRAMS'
meshes = []
need_offset = True
for model in layout_data["models"]:
transform = model.get('transform', {})
homo_matrix = transform["homo_matrix"]
reconstructed_matrix = np.array(homo_matrix, dtype=np.float64)
obj_name = model.get('file_name', '')
obj_path = os.path.join(original_obj_pid_dir, obj_name)
mtl_name_temp = obj_name
separator = "_P"
index = mtl_name_temp.find(separator)
if index != -1:
old_mtl_name = mtl_name_temp[:index]
else:
old_mtl_name = mtl_name_temp # 或者你希望的其他处理逻辑,比如直接使用原字符串或报错
old_mtl_name = f"{old_mtl_name}.mtl"
old_mtl_path = os.path.join(original_obj_pid_dir, old_mtl_name)
bpy.ops.wm.obj_import(filepath=obj_path)
imported_object = bpy.context.object
if imported_object is None or imported_object.type != 'MESH':
print(f"警告: 未能成功导入网格对象 {obj_name}。跳过。")
continue
mesh_data = imported_object.data
mesh_data.calc_loop_triangles()
original_vertices = np.empty(len(mesh_data.vertices) * 3, dtype=np.float64)
mesh_data.vertices.foreach_get('co', original_vertices)
original_vertices = original_vertices.reshape(-1, 3)
transformed_vertices = mesh_transform_by_matrix(original_vertices, reconstructed_matrix)
if need_offset:
offset = np.array([-small_machine_size[0], -small_machine_size[1], 0])
transformed_vertices += offset
print(f"已对模型 {obj_name} 应用偏移: {offset}")
flattened_verts = transformed_vertices.reshape(-1)
mesh_data.vertices.foreach_set('co', flattened_verts)
mesh_data.update()
meshes.append(imported_object)
# 导出前确保选中当前对象
bpy.ops.object.select_all(action='DESELECT')
imported_object.select_set(True)
bpy.context.view_layer.objects.active = imported_object
bpy.ops.wm.obj_export(
filepath=obj_path,
export_selected_objects=True,
export_materials=True,
path_mode='AUTO'
)
os.remove(old_mtl_path)
def transform_save_o3d(layout_data, original_obj_pid_dir):
meshes = []
# 小打印机需要偏移[-small_machine_size[0], -small_machine_size[1], 0]
need_offset = True
for model in layout_data["models"]:
transform = model.get('transform', {})
homo_matrix = transform["homo_matrix"] # 获取存储的列表
reconstructed_matrix = np.array(homo_matrix, dtype=np.float64)
obj_name = model.get('file_name', '')
obj_path = os.path.join(original_obj_pid_dir, obj_name)
# 加载网格
try:
mesh = o3d.io.read_triangle_mesh(obj_path, enable_post_processing=True)
if not mesh.has_vertices():
print(f"警告: 网格无有效顶点 - {obj_path}")
continue
except Exception as e:
print(f"加载模型失败: {obj_path} - {e}")
continue
original_vertices = np.asarray(mesh.vertices)
transformed_vertices = mesh_transform_by_matrix(original_vertices, reconstructed_matrix)
# 如果 need_offset 为 True,应用额外的偏移
if need_offset:
offset = np.array([-small_machine_size[0], -small_machine_size[1], 0])
transformed_vertices += offset
print(f"已对模型 {obj_name} 应用偏移: {offset}")
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices)
meshes.append(mesh)
obj_path_arrange = os.path.join(original_obj_pid_dir, "arrange")
if not os.path.exists(obj_path_arrange):
os.mkdir(obj_path_arrange)
obj_path_arrange_obj = os.path.join(obj_path_arrange, obj_name)
print("obj_path_arrange_obj", obj_path_arrange_obj)
mesh.compute_vertex_normals()
o3d.io.write_triangle_mesh(obj_path_arrange_obj, mesh,write_triangle_uvs=True)
voxel_size = 3
def is_multi_obj(obj_name):
pattern = r'_x(\d+)'
match = re.search(pattern, obj_name)
if match:
number = match.group(1)
if int(number) > 1 :
return True
return False
def is_same_obj(obj_name1, obj_name2):
pre_name1 = obj_name1.split("_x")[0]
pre_name2 = obj_name2.split("_x")[0]
print(f"pre_name1={pre_name1}, pre_name2={pre_name2}")
if (pre_name1==pre_name2):
return True
return False
# -------------------------- 结束:模型 ----------------------------------
# -------------------------- 开始:碰撞检测和越界 --------------------------
extend_dist_border_x_min = 6
extend_dist_border_x_max = 3
extend_dist_border_z_max = 3
extend_dist_border_y_min = 6
extend_dist_border_y_max = 6
extend_dist_model_x = 4
extend_dist_model_y = 2
extend_dist_model_z = 2
extend_dist_box = 1
def is_cross_border_c(x, y, z, mx, my, mz, max_x, max_y, max_z):
if (x - mx < extend_dist_border_x_min or
y - my < extend_dist_border_y_min or
z + mz > max_z - extend_dist_border_z_max or
y > max_y - extend_dist_border_y_max):
return True
return False
# -------------------------- 结束:碰撞检测和越界 --------------------------

847
get_lowest_position_of_center_ext.py

@ -1,847 +0,0 @@ @@ -1,847 +0,0 @@
import open3d as o3d
import numpy as np
import copy
import time
from get_lowest_position_of_z_out import get_lowest_position_of_z_out
# 对外部提供的获取最低z的接口
def get_lowest_position_of_center_out2(obj_path):
total_matrix = np.eye(4)
mesh_obj = o3d.io.read_triangle_mesh(obj_path)
voxel_size = 3
return get_lowest_position_of_center(mesh_obj, obj_path, total_matrix, voxel_size)
def calculate_rotation_and_center_of_mass(angle_x, angle_y, angle_z, points):
"""计算某一组旋转角度后的重心"""
# 计算绕X轴、Y轴和Z轴的旋转矩阵
R_x = np.array([
[1, 0, 0],
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
])
R_y = np.array([
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
[0, 1, 0],
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
])
R_z = np.array([
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
[0, 0, 1]
])
# 综合旋转矩阵
R = R_z @ R_y @ R_x
# 执行旋转
rotated_points = points @ R.T
# 计算最小z值
min_z = np.min(rotated_points[:, 2])
# 计算平移向量,将最小Z值平移到0
translation_vector = np.array([0, 0, -min_z])
rotated_points += translation_vector
# 计算重心
center_of_mass = np.mean(rotated_points, axis=0)
return center_of_mass[2], angle_x, angle_y, angle_z
def parallel_rotation4(points, angle_step=4):
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)"""
min_center = float('inf')
for angle_x in range(-45, 45, angle_step):
for angle_y in range(0, 360, angle_step):
center_z, ax, ay, _ = calculate_rotation_and_center_of_mass(angle_x, angle_y, 0, points)
if center_z < min_center:
min_center = center_z
best_angle_x = ax
best_angle_y = ay
return (best_angle_x, best_angle_y, 0, min_center)
import numpy as np
from numba import cuda
import math
# CUDA核函数:计算所有旋转角度下的重心高度
@cuda.jit
def compute_centers_kernel(points, centers, angle_x_start, angle_x_step, angle_y_start, angle_y_step, num_x, num_y):
i = cuda.blockIdx.x * cuda.blockDim.x + cuda.threadIdx.x
j = cuda.blockIdx.y * cuda.blockDim.y + cuda.threadIdx.y
if i >= num_x or j >= num_y:
return
# 获取整数角度值
angle_x = angle_x_start + i * angle_x_step
angle_y = angle_y_start + j * angle_y_step
rx = math.radians(float(angle_x)) # 使用 float() 进行转换
ry = math.radians(float(angle_y))
rz = 0.0
# 计算旋转矩阵
cos_x = math.cos(rx)
sin_x = math.sin(rx)
cos_y = math.cos(ry)
sin_y = math.sin(ry)
cos_z = math.cos(rz)
sin_z = math.sin(rz)
# 旋转矩阵: R = R_z * R_y * R_x
R00 = cos_z * cos_y
R01 = cos_z * sin_y * sin_x - sin_z * cos_x
R02 = cos_z * sin_y * cos_x + sin_z * sin_x
R10 = sin_z * cos_y
R11 = sin_z * sin_y * sin_x + cos_z * cos_x
R12 = sin_z * sin_y * cos_x - cos_z * sin_x
R20 = -sin_y
R21 = cos_y * sin_x
R22 = cos_y * cos_x
n = points.shape[0]
min_z = 1e10
# 第一遍:计算最小Z值
for k in range(n):
x = points[k, 0]
y = points[k, 1]
z = points[k, 2]
x_rot = R00 * x + R01 * y + R02 * z
y_rot = R10 * x + R11 * y + R12 * z
z_rot = R20 * x + R21 * y + R22 * z
if z_rot < min_z:
min_z = z_rot
total_z = 0.0
# 第二遍:平移并计算Z坐标和
for k in range(n):
x = points[k, 0]
y = points[k, 1]
z = points[k, 2]
x_rot = R00 * x + R01 * y + R02 * z
y_rot = R10 * x + R11 * y + R12 * z
z_rot = R20 * x + R21 * y + R22 * z
z_trans = z_rot - min_z
total_z += z_trans
center_z = total_z / n
centers[i, j] = center_z
# CUDA版本的并行旋转计算
def parallel_rotation4_cuda(points, angle_step=4):
angle_x_start = -45
angle_x_end = 45
angle_y_start = 0
angle_y_end = 360
num_x = int((angle_x_end - angle_x_start) / angle_step)
num_y = int((angle_y_end - angle_y_start) / angle_step)
# 将点云数据复制到GPU
d_points = cuda.to_device(points.astype(np.float32))
d_centers = cuda.device_array((num_x, num_y), dtype=np.float32)
# 配置线程块和网格
threadsperblock = (16, 16)
blockspergrid_x = (num_x + threadsperblock[0] - 1) // threadsperblock[0]
blockspergrid_y = (num_y + threadsperblock[1] - 1) // threadsperblock[1]
blockspergrid = (blockspergrid_x, blockspergrid_y)
# 启动核函数
compute_centers_kernel[blockspergrid, threadsperblock](
d_points, d_centers, angle_x_start, angle_step, angle_y_start, angle_step, num_x, num_y
)
# 将结果复制回主机
centers = d_centers.copy_to_host()
# 找到最小重心值的索引
min_index = np.argmin(centers)
i = min_index // num_y
j = min_index % num_y
best_angle_x = angle_x_start + i * angle_step
best_angle_y = angle_y_start + j * angle_step
min_center = centers[i, j]
return best_angle_x, best_angle_y, 0, min_center
def read_mesh(obj_path, simple=True):
mesh_obj = o3d.io.read_triangle_mesh(obj_path)
return mesh_obj
def compute_mesh_center2(vertices):
"""
计算网格质心
参数:
vertices: 顶点坐标数组形状为(N, 3)的NumPy数组或列表
返回:
centroid: 质心坐标的NumPy数组 [x, y, z]
"""
if len(vertices) == 0:
raise ValueError("顶点数组不能为空")
n = len(vertices) # 顶点数量
# 初始化坐标累加器
sum_x, sum_y, sum_z = 0.0, 0.0, 0.0
start_time1 = time.time()
# 遍历所有顶点累加坐标值
for vertex in vertices:
sum_x += vertex[0]
sum_y += vertex[1]
sum_z += vertex[2]
print("compute_mesh_center1 time", time.time()-start_time1)
# 计算各坐标轴的平均值
centroid = np.array([sum_x / n, sum_y / n, sum_z / n])
return centroid
def compute_mesh_center(vertices):
"""
计算网格质心优化版
参数:
vertices: 顶点坐标数组形状为(N, 3)的NumPy数组或列表
返回:
centroid: 质心坐标的NumPy数组 [x, y, z]
"""
if len(vertices) == 0:
raise ValueError("顶点数组不能为空")
# 确保vertices是NumPy数组
vertices_np = np.asarray(vertices)
# 使用NumPy的mean函数直接计算均值(向量化操作)
centroid = np.mean(vertices_np, axis=0)
return centroid
def get_lowest_position_of_center_ext3(mesh_obj, obj_path, voxel_size = 3):
best_angle_x, best_angle_y, best_angle_z, z_mean_min, pcd_transformed= get_lowest_position_of_center2(mesh_obj, obj_path, voxel_size)
return best_angle_x, best_angle_y, best_angle_z, z_mean_min
def get_lowest_position_of_center_ext2(mesh_obj, obj_path, total_matrix, voxel_size):
# total_matrix, z_mean_min = get_lowest_position_of_center(obj_path, total_matrix, voxel_size)
temp_matrix, z_mean_min = get_lowest_position_of_center(mesh_obj, obj_path, np.eye(4), voxel_size)
# print("temp_matrix=",temp_matrix,voxel_size,mesh_obj)
total_matrix = temp_matrix @ total_matrix
return total_matrix, z_mean_min
def get_lowest_position_of_center_ext(obj_path, total_matrix):
temp_matrix, z_max = get_lowest_position_of_z_out(obj_path)
total_matrix = temp_matrix @ total_matrix
return total_matrix, z_max
def down_sample(pcd, voxel_size, farthest_sample = False):
original_num = len(pcd.points)
target_samples = 1500 # 1000
num_samples = min(target_samples, original_num)
# 第一步:使用体素下采样快速减少点数量
# voxel_size = 3
if farthest_sample:
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_voxel = pcd.voxel_down_sample(voxel_size)
down_num = len(pcd_voxel.points)
# print(f"original_num={original_num}, down_num={down_num}")
# 第二步:仅在必要时进行最远点下采样
if len(pcd_voxel.points) > target_samples and False:
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_downsampled = pcd_voxel
return pcd_downsampled
def get_lowest_position_of_center(mesh_obj, obj_path, total_matrix, voxel_size):
# print(f"obj_path={obj_path}, get_lowest_position_of_center voxel_size={voxel_size}")
start_time1 = time.time()
vertices = np.asarray(mesh_obj.vertices)
# 确保网格有顶点
if len(vertices) == 0:
# raise ValueError(f"Mesh has no vertices: {obj_path}")
print(f"Warning: Mesh has no vertices: {mesh_obj}")
return None
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# print("voxel_size",voxel_size,obj_path, len(pcd.points), len(mesh_obj.vertices))
# 对点云进行下采样(体素网格法)
#"""
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
if len(np.asarray(pcd_downsampled.points)) <= 0:
bbox = pcd.get_axis_aligned_bounding_box()
volume = bbox.volume()
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}")
# 处理体积为零的情况
if volume <= 0:
# 计算点云的实际范围
points = np.asarray(pcd.points)
if len(points) > 0:
min_bound = np.min(points, axis=0)
max_bound = np.max(points, axis=0)
extent = max_bound - min_bound
# 确保最小维度至少为0.01
min_dimension = max(0.01, np.min(extent))
volume = min_dimension ** 3
else:
volume = 1.0 # 最后的安全回退
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}")
# 安全计算密度 - 防止除零错误
if len(pcd.points) > 0 and volume > 0:
original_density = len(pcd.points) / volume
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33)))
else:
# 当点数为0或体积为0时使用默认体素大小
voxel_size = 1.0 # 默认值
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}")
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
original_num = len(pcd.points)
target_samples = 1000
num_samples = min(target_samples, original_num)
# print("get_lowest_position_of_center1 time", time.time()-start_time1)
start_time2 = time.time()
# 确保下采样后有点云
if len(np.asarray(pcd_downsampled.points)) == 0:
# 使用原始点云作为后备
pcd_downsampled = pcd
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points")
points = np.asarray(pcd_downsampled.points)
# 初始化最小重心Y的值
min_center_of_mass_y = float('inf')
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation4(points, angle_step=3)
# 使用最佳角度进行旋转并平移obj
pcd_transformed = copy.deepcopy(mesh_obj)
# 最佳角度旋转
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x))
pcd_transformed.rotate(R_x)
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y))
pcd_transformed.rotate(R_y)
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z))
pcd_transformed.rotate(R_z)
T_x = np.eye(4)
T_x[:3, :3] = R_x
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_x @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
T_y = np.eye(4)
T_y[:3, :3] = R_y
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_y @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
T_z = np.eye(4)
T_z[:3, :3] = R_z
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_z @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
#试着旋转180,让脸朝上
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
T_trans1 = np.eye(4)
T_trans1[:3, 3] = translation_vector
total_matrix = T_trans1 @ total_matrix
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean1 = np.mean(vertices[:, 2])
z_max1 = np.max(vertices[:, 2])
angle_rad = np.pi
#print("旋转前质心:", pcd_transformed.get_center())
#print("旋转前点示例:", np.asarray(pcd_transformed.vertices)[:3])
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
centroid = pcd_transformed.get_center()
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
aabb = pcd_transformed.get_axis_aligned_bounding_box()
# center_point = aabb.get_center()
center_point = compute_mesh_center(mesh_obj.vertices)
# 构建绕中心点旋转的变换矩阵[3](@ref)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
R_y180 = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
T_rotate = np.eye(4)
T_rotate[:3, :3] = R_y180
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_rotate @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
#print("旋转后质心:", pcd_transformed.get_center())
#print("旋转后点示例:", np.asarray(pcd_transformed.vertices)[:3])
#
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
max_z = np.max(vertices[:, 2])
# print("min_z1", min_z, obj_path)
translation_vector = np.array([0,0,-min_z,])
# translation_vector = np.array([0,0,-min_z + (min_z-max_z),])
# print("translation_vector1",translation_vector)
pcd_transformed.translate(translation_vector)
T_trans2 = np.eye(4)
T_trans2[:3, 3] = translation_vector
translation = total_matrix[:3, 3]
# print("translation_vector2",translation_vector)
# print(1,translation)
total_matrix = T_trans2 @ total_matrix
translation = total_matrix[:3, 3]
# print(2,translation)
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean2 = np.mean(vertices[:, 2])
z_max2 = np.max(vertices[:, 2])
# print(f"get_lowest_position_of_center z_max1={z_max1}, z_max2={z_max2}, len={len(pcd_transformed.vertices)}, obj_path={obj_path}")
if (z_mean2 > z_mean1):
# if (z_max2 > z_max1):
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
centroid = pcd_transformed.get_center()
aabb = pcd_transformed.get_axis_aligned_bounding_box()
# center_point = aabb.get_center()
center_point = compute_mesh_center(mesh_obj.vertices)
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
# 构建反向旋转矩阵
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
T_rotate_inv = np.eye(4)
T_rotate_inv[:3, :3] = R_y
# 完整的反向绕中心旋转矩阵
T_rot_center_inv = T_origin_to_center @ T_rotate_inv @ T_center_to_origin
total_matrix = T_rot_center_inv @ total_matrix
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
# print("min_z2", min_z, obj_path)
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
T_trans3 = np.eye(4)
T_trans3[:3, 3] = translation_vector
total_matrix = T_trans3 @ total_matrix
# z_mean_min = min(z_mean1, z_mean2)
z_max_min = min(z_max1, z_max2)
# print("get_lowest_position_of_center2 time", time.time()-start_time2)
return total_matrix, z_max_min
import requests
import json
import re
def is_valid_float_string(s):
# 匹配科学计数法或普通小数,允许开头和末尾的空格
pattern = r'^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$'
return bool(re.match(pattern, s.strip()))
def safe_convert_to_float(s):
"""
尝试将字符串安全转换为float处理一些不完整情况
"""
s = s.strip().lower() # 去除空格,统一为小写
# 处理空字符串或完全非数字的情况
if not s or s in ['na', 'nan', 'inf', 'null', 'none']:
return None # 或者可以根据需要返回 float('nan')
# 检查是否为有效的浮点数字符串
if is_valid_float_string(s):
return float(s)
# 处理类似 '0.00000000e' 的情况(缺少指数)
if s.endswith('e'):
# 尝试添加指数 '0' -> 'e0'
try:
return float(s + '0')
except ValueError:
pass # 如果添加'e0'后仍然失败,则继续下面的异常处理
# 更激进的清理:移除非数字、小数点、负号和指数e以外的所有字符
# 注意:这可能破坏有特定格式的字符串,慎用
cleaned_s = re.sub(r'[^\d\.eE-]', '', s)
try:
return float(cleaned_s)
except ValueError:
pass
# 如果所有尝试都失败,返回None或抛出异常
return None
def string_to_matrix(data_string):
"""
将字符串转换为NumPy浮点数矩阵并处理可能的转换错误
"""
# 分割字符串
lines = data_string.strip().split(';')
matrix = []
for line in lines:
num_list = line.split()
float_row = []
for num in num_list:
# 使用安全转换函数
value = safe_convert_to_float(num)
if value is None:
# 处理转换失败,例如记录日志、使用NaN代替
print(f"警告: 无法转换字符串 '{num}',将其替换为NaN。")
value = np.nan # 用NaN标记缺失或无效值
float_row.append(value)
matrix.append(float_row)
return np.array(matrix)
import ast
def get_lowest_position_of_center_net(printId, total_matrix):
print("get_lowest_position_of_center_net", printId)
url = f"https://mp.api.suwa3d.com/api/printOrder/infoByPrintId?printId={printId}"
res = requests.get(url)
datas = res.json()["data"]["layout"]
print("datas=", datas)
homo_matrix_str = datas.get("homo_matrix")
print("homo_matrix_str=", homo_matrix_str)
# 1. 去除字符串首尾的方括号
str_cleaned = homo_matrix_str.strip('[]')
# 2. 按行分割字符串
rows = str_cleaned.split('\n')
# 3. 修复:处理每行中的逗号问题
matrix_list = []
for row in rows:
if row.strip() == '':
continue
# 去除行首尾的方括号和空格
row_cleaned = row.strip(' []')
# 按逗号分割,但过滤掉空字符串
elements = [elem.strip() for elem in row_cleaned.split(',') if elem.strip() != '']
# 进一步清理每个元素:去除可能残留的逗号和方括号
cleaned_elements = []
for elem in elements:
# 去除元素中可能存在的逗号、方括号和空格
elem_cleaned = elem.strip(' ,[]')
if elem_cleaned != '':
cleaned_elements.append(elem_cleaned)
if cleaned_elements: # 只添加非空行
matrix_list.append(cleaned_elements)
print("matrix_list=", matrix_list)
# 4. 安全地转换为浮点数数组(带错误处理)
try:
reconstructed_matrix = np.array(matrix_list, dtype=float)
except ValueError as e:
print(f"转换矩阵时出错: {e}")
print("尝试逐个元素转换...")
# 逐个元素转换,便于定位问题元素
float_matrix = []
for i, row in enumerate(matrix_list):
float_row = []
for j, elem in enumerate(row):
try:
# 再次清理元素并转换
cleaned_elem = elem.strip(' ,')
float_val = float(cleaned_elem)
float_row.append(float_val)
except ValueError as ve:
print(f"无法转换的元素: 行{i}, 列{j}, 值'{elem}', 错误: {ve}")
# 可以选择设置为0或NaN,或者抛出异常
float_row.append(0.0) # 或者 np.nan
float_matrix.append(float_row)
reconstructed_matrix = np.array(float_matrix, dtype=float)
layout_z = datas.get("layout_z", 0)
print("layout_z", layout_z)
reconstructed_matrix = reconstructed_matrix @ total_matrix
print("reconstructed_matrix=", reconstructed_matrix)
return reconstructed_matrix, layout_z
def get_lowest_position_of_center2(mesh_obj, obj_path, voxel_size = 3):
# mesh_obj = read_mesh(obj_path)
vertices = np.asarray(mesh_obj.vertices)
# 确保网格有顶点
if len(vertices) == 0:
print(f"Warning: Mesh has no vertices: {obj_path}")
return None
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# 对点云进行下采样(体素网格法
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
if len(np.asarray(pcd_downsampled.points)) <= 0:
bbox = pcd.get_axis_aligned_bounding_box()
volume = bbox.volume()
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}")
# 处理体积为零的情况
if volume <= 0:
# 计算点云的实际范围
points = np.asarray(pcd.points)
if len(points) > 0:
min_bound = np.min(points, axis=0)
max_bound = np.max(points, axis=0)
extent = max_bound - min_bound
# 确保最小维度至少为0.01
min_dimension = max(0.01, np.min(extent))
volume = min_dimension ** 3
else:
volume = 1.0 # 最后的安全回退
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}")
# 安全计算密度 - 防止除零错误
if len(pcd.points) > 0 and volume > 0:
original_density = len(pcd.points) / volume
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33)))
else:
# 当点数为0或体积为0时使用默认体素大小
voxel_size = 1.0 # 默认值
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}")
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
# 确保下采样后有点云
if len(np.asarray(pcd_downsampled.points)) == 0:
# 使用原始点云作为后备
pcd_downsampled = pcd
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points")
points = np.asarray(pcd_downsampled.points)
# 初始化最小重心Y的值
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
# 旋转并计算最优角度:绕X、Y、Z轴进行每度的旋转
best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation4(points, angle_step=3)
# print("best_angle1", best_angle_x, best_angle_y, best_angle_z)
# 使用最佳角度进行旋转并平移obj
pcd_transformed = copy.deepcopy(mesh_obj)
# 最佳角度旋转
"""
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x))
pcd_transformed.rotate(R_x)
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y))
pcd_transformed.rotate(R_y)
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z))
pcd_transformed.rotate(R_z)
#"""
aabb = pcd_transformed.get_axis_aligned_bounding_box()
center_point = aabb.get_center()
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean1 = np.mean(vertices[:, 2])
angle_rad = np.pi
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
best_angle_y += angle_rad / np.pi * 180
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean2 = np.mean(vertices[:, 2])
# print("z_mean",z_mean1,z_mean2,len(pcd_transformed.vertices),obj_path)
if z_mean2 > z_mean1:
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
aabb = pcd_transformed.get_axis_aligned_bounding_box()
center_point = aabb.get_center()
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
best_angle_y += -angle_rad / np.pi * 180
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
z_mean_min = min(z_mean1, z_mean2)
angle_z_delta = arrange_box_correctly(pcd_transformed, voxel_size)
best_angle_z += angle_z_delta
# print("angle_z_delta", angle_z_delta, best_angle_z)
return best_angle_x, best_angle_y, best_angle_z, z_mean_min, pcd_transformed
def arrange_box_correctly(obj_transformed, voxel_size):
vertices = np.asarray(obj_transformed.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# 降采样与特征计算
pcd_downsampled = down_sample(pcd, voxel_size)
original_num = len(pcd.points)
target_samples = 1000
num_samples = min(target_samples, original_num)
points = np.asarray(pcd_downsampled.points)
cov = np.cov(points.T)
center = obj_transformed.get_center()
# 特征分解与方向约束(关键修改点)
eigen_vals, eigen_vecs = np.linalg.eigh(cov)
max_axis = eigen_vecs[:, np.argmax(eigen_vals)]
# 强制主方向向量X分量为正(指向右侧)
if max_axis[0] < 0 or (max_axis[0] == 0 and max_axis[1] < 0):
max_axis = -max_axis
target_dir = np.array([1, 0]) # 目标方向为X正轴
current_dir = max_axis[:2] / np.linalg.norm(max_axis[:2])
dot_product = np.dot(current_dir, target_dir)
# print("dot_product", dot_product)
if dot_product < 0.8: # 阈值控制方向敏感性(建议0.6~0.9)
max_axis = -max_axis # 强制翻转方向
# 计算旋转角度
angle_z = np.arctan2(max_axis[1], max_axis[0]) % (2 * np.pi)
if max_axis[0] <= 0 and max_axis[1] <= 0:
angle_z += np.pi
# print("max_axis2", max_axis, -angle_z, np.rad2deg(-angle_z))
R = o3d.geometry.get_rotation_matrix_from_axis_angle([0, 0, -angle_z])
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = center - R.dot(center) # 保持中心不变
obj_transformed.transform(T)
return np.rad2deg(-angle_z)

352
get_lowest_position_of_z_out.py

@ -1,352 +0,0 @@ @@ -1,352 +0,0 @@
import open3d as o3d
import numpy as np
import copy
import time
import argparse
"""
对外部提供的获取最低z的接口
get_lowest_position_of_z_out
参数:
obj_path, 模型数据路径
返回:
total_matrix: 旋转矩阵
z_max: Z最高点
"""
def get_lowest_position_of_z_out(obj_path):
mesh_obj = o3d.io.read_triangle_mesh(obj_path)
total_matrix = np.eye(4)
voxel_size = 3
# print(f"obj_path={obj_path}, get_lowest_position_of_center voxel_size={voxel_size}")
start_time1 = time.time()
vertices = np.asarray(mesh_obj.vertices)
# 确保网格有顶点
if len(vertices) == 0:
# raise ValueError(f"Mesh has no vertices: {obj_path}")
print(f"Warning: Mesh has no vertices: {mesh_obj}")
return None
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# print("voxel_size",voxel_size,obj_path, len(pcd.points), len(mesh_obj.vertices))
# 对点云进行下采样(体素网格法)
#"""
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
if len(np.asarray(pcd_downsampled.points)) <= 0:
bbox = pcd.get_axis_aligned_bounding_box()
volume = bbox.volume()
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}")
# 处理体积为零的情况
if volume <= 0:
# 计算点云的实际范围
points = np.asarray(pcd.points)
if len(points) > 0:
min_bound = np.min(points, axis=0)
max_bound = np.max(points, axis=0)
extent = max_bound - min_bound
# 确保最小维度至少为0.01
min_dimension = max(0.01, np.min(extent))
volume = min_dimension ** 3
else:
volume = 1.0 # 最后的安全回退
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}")
# 安全计算密度 - 防止除零错误
if len(pcd.points) > 0 and volume > 0:
original_density = len(pcd.points) / volume
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33)))
else:
# 当点数为0或体积为0时使用默认体素大小
voxel_size = 1.0 # 默认值
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}")
pcd_downsampled = down_sample(pcd, voxel_size)
pcd_downsampled.paint_uniform_color([0, 0, 1])
original_num = len(pcd.points)
target_samples = 1000
num_samples = min(target_samples, original_num)
# print("get_lowest_position_of_center1 time", time.time()-start_time1)
start_time2 = time.time()
# 确保下采样后有点云
if len(np.asarray(pcd_downsampled.points)) == 0:
# 使用原始点云作为后备
pcd_downsampled = pcd
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points")
points = np.asarray(pcd_downsampled.points)
# 初始化最小重心Y的值
max_z_of_mass_y = float('inf')
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
best_angle_x, best_angle_y, best_angle_z, max_z_of_mass_y = parallel_rotation(points, angle_step=3)
# 使用最佳角度进行旋转并平移obj
pcd_transformed = copy.deepcopy(mesh_obj)
# 最佳角度旋转
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x))
pcd_transformed.rotate(R_x)
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y))
pcd_transformed.rotate(R_y)
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z))
pcd_transformed.rotate(R_z)
T_x = np.eye(4)
T_x[:3, :3] = R_x
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_x @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
T_y = np.eye(4)
T_y[:3, :3] = R_y
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_y @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
T_z = np.eye(4)
T_z[:3, :3] = R_z
center_point = compute_mesh_center(mesh_obj.vertices)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_z @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
#试着旋转180,让脸朝上
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
T_trans1 = np.eye(4)
T_trans1[:3, 3] = translation_vector
total_matrix = T_trans1 @ total_matrix
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean1 = np.mean(vertices[:, 2])
z_max1 = np.max(vertices[:, 2])
angle_rad = np.pi
#print("旋转前质心:", pcd_transformed.get_center())
#print("旋转前点示例:", np.asarray(pcd_transformed.vertices)[:3])
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
centroid = pcd_transformed.get_center()
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
aabb = pcd_transformed.get_axis_aligned_bounding_box()
# center_point = aabb.get_center()
center_point = compute_mesh_center(mesh_obj.vertices)
# 构建绕中心点旋转的变换矩阵[3](@ref)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
R_y180 = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
T_rotate = np.eye(4)
T_rotate[:3, :3] = R_y180
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
T_rot_center = T_origin_to_center @ T_rotate @ T_center_to_origin
total_matrix = T_rot_center @ total_matrix
#print("旋转后质心:", pcd_transformed.get_center())
#print("旋转后点示例:", np.asarray(pcd_transformed.vertices)[:3])
#
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
max_z = np.max(vertices[:, 2])
# print("min_z1", min_z, obj_path)
translation_vector = np.array([0,0,-min_z,])
# translation_vector = np.array([0,0,-min_z + (min_z-max_z),])
# print("translation_vector1",translation_vector)
pcd_transformed.translate(translation_vector)
T_trans2 = np.eye(4)
T_trans2[:3, 3] = translation_vector
translation = total_matrix[:3, 3]
# print("translation_vector2",translation_vector)
# print(1,translation)
total_matrix = T_trans2 @ total_matrix
translation = total_matrix[:3, 3]
# print(2,translation)
# 计算 z 坐标均值
vertices = np.asarray(pcd_transformed.vertices)
z_mean2 = np.mean(vertices[:, 2])
z_max2 = np.max(vertices[:, 2])
# print(f"get_lowest_position_of_center z_max1={z_max1}, z_max2={z_max2}, len={len(pcd_transformed.vertices)}, obj_path={obj_path}")
if (z_mean2 > z_mean1):
# if (z_max2 > z_max1):
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
centroid = pcd_transformed.get_center()
aabb = pcd_transformed.get_axis_aligned_bounding_box()
# center_point = aabb.get_center()
center_point = compute_mesh_center(mesh_obj.vertices)
pcd_transformed.translate(-center_point)
pcd_transformed.rotate(R_y, center=(0, 0, 0))
pcd_transformed.translate(center_point)
T_center_to_origin = np.eye(4)
T_center_to_origin[:3, 3] = -center_point
T_origin_to_center = np.eye(4)
T_origin_to_center[:3, 3] = center_point
# 构建反向旋转矩阵
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
T_rotate_inv = np.eye(4)
T_rotate_inv[:3, :3] = R_y
# 完整的反向绕中心旋转矩阵
T_rot_center_inv = T_origin_to_center @ T_rotate_inv @ T_center_to_origin
total_matrix = T_rot_center_inv @ total_matrix
vertices = np.asarray(pcd_transformed.vertices)
# 计算平移向量,将最小Y值平移到0
min_z = np.min(vertices[:, 2])
# print("min_z2", min_z, obj_path)
translation_vector = np.array([0,0,-min_z,])
pcd_transformed.translate(translation_vector)
T_trans3 = np.eye(4)
T_trans3[:3, 3] = translation_vector
total_matrix = T_trans3 @ total_matrix
# z_mean_min = min(z_mean1, z_mean2)
z_max = min(z_max1, z_max2)
# print("get_lowest_position_of_center2 time", time.time()-start_time2)
return total_matrix, z_max
def calculate_rotation_and_top_of_mass(angle_x, angle_y, angle_z, points):
"""计算某一组旋转角度后的重心"""
# 计算绕X轴、Y轴和Z轴的旋转矩阵
R_x = np.array([
[1, 0, 0],
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
])
R_y = np.array([
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
[0, 1, 0],
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
])
R_z = np.array([
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
[0, 0, 1]
])
# 综合旋转矩阵
R = R_z @ R_y @ R_x
# 执行旋转
rotated_points = points @ R.T
# 计算最小z值
min_z = np.min(rotated_points[:, 2])
# 计算平移向量,将最小Z值平移到0
translation_vector = np.array([0, 0, -min_z])
rotated_points += translation_vector
top_of_mass = np.max(rotated_points, axis=0)
return top_of_mass[2], angle_x, angle_y, angle_z
def parallel_rotation(points, angle_step=4):
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)"""
max_top = float('inf')
for angle_x in range(-90, 90, angle_step):
for angle_y in range(0, 360, angle_step):
max_z, ax, ay, _ = calculate_rotation_and_top_of_mass(angle_x, angle_y, 0, points)
if max_z < max_top:
max_top = max_z
best_angle_x = ax
best_angle_y = ay
return (best_angle_x, best_angle_y, 0, max_top)
def compute_mesh_center(vertices):
if len(vertices) == 0:
raise ValueError("顶点数组不能为空")
# 确保vertices是NumPy数组
vertices_np = np.asarray(vertices)
# 使用NumPy的mean函数直接计算均值(向量化操作)
centroid = np.mean(vertices_np, axis=0)
return centroid
def down_sample(pcd, voxel_size, farthest_sample = False):
original_num = len(pcd.points)
target_samples = 1500 # 1000
num_samples = min(target_samples, original_num)
# 第一步:使用体素下采样快速减少点数量
# voxel_size = 3
if farthest_sample:
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_voxel = pcd.voxel_down_sample(voxel_size)
down_num = len(pcd_voxel.points)
# print(f"original_num={original_num}, down_num={down_num}")
# 第二步:仅在必要时进行最远点下采样
if len(pcd_voxel.points) > target_samples and False:
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_downsampled = pcd_voxel
return pcd_downsampled
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("--obj_path", type=str, required=True, help="batchobj_path_id")
args = parser.parse_args()
obj_path = args.obj_path
max, z = get_lowest_position_of_z_out(obj_path)

179
grid_near_three.py

@ -1,179 +0,0 @@ @@ -1,179 +0,0 @@
import os
import time
import open3d as o3d
import numpy as np
def make_near_dict(base_dir,compact_dir):
""""""
# 用于存储结果的字典
results = {}
# 遍历目录中的所有 .ply 文件
for ply_file in os.listdir(base_dir):
# 检查文件是否为 .ply 格式
if ply_file.endswith('.ply'):
ply_path = os.path.join(base_dir, ply_file)
compact_ply_path = os.path.join(compact_dir, ply_file)
if os.path.exists(compact_ply_path):
ply_read_path = compact_ply_path
else:
ply_read_path = ply_path
# 读取点云
pcd = o3d.io.read_point_cloud(ply_read_path)
# 获取点云的点数据
points = np.asarray(pcd.points)
# 计算质心
centroid = np.mean(points, axis=0)
# 计算 Y 轴最小值
min_y_value = np.min(points[:, 1]) # Y 轴最小值
max_y_value = np.max(points[:, 1])
# 计算 X 轴最小值
min_x_value = np.min(points[:, 0]) # X 轴最小值
max_x_value = np.max(points[:, 0]) # X 轴最小值
#ply_pid = ply_file.split("_")[0]
# 将结果存入字典
results[ply_file] = {
"centroid": centroid,
"min_x_value": min_x_value,
"min_y_value": min_y_value,
"max_x_value": max_x_value,
"max_y_value": max_y_value,
}
# 打印结果
# for ply_file, values in results.items():
# print(f"文件: {ply_file}")
# print(f" 质心: {values['centroid']}")
# print(f" X 轴最小值: {values['min_x_value']}")
# print(f" Y 轴最小值: {values['min_y_value']}")
# 计算每个ply需要触碰检测的
check_touch_dict = {}
for ply_file in results.keys():
print(ply_file)
#ply_pid = ply_file.split("_")[0]
#print(ply_pid)
bounds_min_x = results[ply_file]["min_x_value"]
bounds_min_y = results[ply_file]["min_y_value"]
#bounds_center = results[ply_file]["centroid"]
need_check_list = []
need_values_dict = {}
for ply_file_near in results.keys():
#print(ply_file_near)
if ply_file!= ply_file_near:
bounds_max_x = results[ply_file_near]["max_x_value"]
bounds_max_y = results[ply_file_near]["max_y_value"]
# if ply_file == "151140_9cm_x1=30.578+41.705+90.753.ply":
# print("-"*50)
# print("主::",ply_file)
# print("从::",ply_file_near)
# print(f"center_x{bounds_center[0]}")
# print(f"center_y{bounds_center[1]}")
# print(f"bounds_max_x{bounds_max_x}")
# print(f"bounds_max_y{bounds_max_y}")
# time.sleep(3)
# 235605_12cm_x1=33.774+30.837+120.344.ply
# if bounds_center[0]<bounds_max_x and bounds_center[1]<bounds_max_y:
# print("添加",ply_file_near)
# need_check_list.append(ply_file_near)
#if bounds_near_center[0]>bounds_min_x and bounds_near_center[1]>bounds_min_y:
#print("-"*50)
# print(f"bounds_min_x{bounds_min_x}")
# print(f"bounds_max_x{bounds_max_x}")
x_dis = bounds_min_x - bounds_max_x
y_dis = bounds_min_y - bounds_max_y
#print(f"x_dis=={x_dis}")
#print(f"y_dis=={y_dis}")
#if ply_file=="158040_15cm_x1=80.682+89.345+152.468.ply":
#if ply_file == "235547_4.8cm_x1=29.339+39.528+57.63.ply":
# print("主::",ply_file)
# print("从::",ply_file_near)
#if ply_file == "158040_15cm_x1=80.682+89.345+152.468.ply":
#if ply_file == "151140_9cm_x1=30.578+41.705+90.753.ply":
# print("主::", ply_file)
# print("临近::", ply_file_near)
# time.sleep(3)
if x_dis<-10 and y_dis<-10:
need_check_list.append(ply_file_near)
need_values_dict["need_check_list"] = need_check_list
# need_values_dict["max_x_value"] = bounds_max_x
# need_values_dict["max_y_value"] = bounds_max_y
check_touch_dict[ply_file] = need_values_dict
# print(check_touch_dict)
# print("开始要计算触碰检测的数据")
# for ply_file, values in check_touch_dict.items():
# print("*"*50)
# print(ply_file)
# print(values)
# 去掉离比较远的数据
for check_touch_key,check_touch_values in check_touch_dict.items():
print("-"*50)
#print(check_touch_key)
#print(check_touch_values["need_check_list"])
need_check_list= check_touch_values["need_check_list"]
#print(len(need_check_list))
if len(need_check_list)>2:
ply_A_path = os.path.join(base_dir, check_touch_key)
compact_ply_path = os.path.join(compact_dir, check_touch_key)
if os.path.exists(compact_ply_path):
ply_read_path = compact_ply_path
else:
ply_read_path = ply_A_path
pcd_A = o3d.io.read_point_cloud(ply_read_path)
points_A = np.asarray(pcd_A.points)
distances = []
for i, check_touch in enumerate(need_check_list):
point = results[check_touch]['centroid']
ply_path = os.path.join(base_dir, check_touch)
# 读取当前点云
pcd = o3d.io.read_point_cloud(ply_path)
points = np.asarray(pcd.points)
# 计算点云之间最小点对距离(brute-force)
diff = points_A[:, np.newaxis, :] - points[np.newaxis, :, :] # (N, M, 3)
dists = np.linalg.norm(diff, axis=2) # (N, M)
min_distance = np.min(dists)
#print(f"check_touch: {check_touch}, centroid: {point}, min_distance: {min_distance:.4f}")
distances.append((i, point, min_distance, check_touch))
distances.sort(key=lambda x: x[2])
# 提取最近的 3 个点
nearest_points = distances[:5]
last_elements = [item[-1] for item in nearest_points]
# print(f"nearest_points---------{nearest_points}")
# print(f"check_touch_key--------{check_touch_key}")
# print(f"last_elements--------{last_elements}")
check_touch_dict[check_touch_key]["need_check_list"] = last_elements
return check_touch_dict
# for check_touch_key,check_touch_values in check_touch_dict.items():
# print("*"*50)
# print(check_touch_key)
# print(check_touch_values)
if __name__ == '__main__':
bounds_fix_out_dir = "/data/datasets_20t/type_setting_test_data/print_bounds_fix_data/"
check_touch_dict=make_near_dict(bounds_fix_out_dir)
print(f"check_touch_dict--------------{check_touch_dict}")
"""
{'need_check_list': ['131508_18cm_x1=51.412+87.921+181.446.ply', '239617_12cm_x1=43.987+54.233+120.691.ply']},
"""

295
output.log

@ -0,0 +1,295 @@ @@ -0,0 +1,295 @@
is_run_local_data=True
pre_batch_id=991112
print_ids=[85240, 88136]
----------------------------------------
----------------------------------------
路径 /home/algo/Documents/print_factory_type/data/ 不是一个有效目录。
目录 /home/algo/Documents/print_factory_type/full/ 下的内容已清空。
从文件读取了 2 个path
开始下载print_model_info: BatchModelInfo(order_id=875986, pid=305425, print_order_id=88136, model_size='9cm_x1', path='objs/download/print/305425/base/model/9cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/305425/base/model/9cm/' 存在,共找到 6 个对象。
已更新材质库引用: 305425Tex1.jpg -> 875986_305425Tex1.jpg
下载文件: objs/download/print/305425/base/model/9cm/305425.mtl -> /home/algo/Documents/print_factory_type/data/991112/875986_305425.mtl
下载文件: objs/download/print/305425/base/model/9cm/305425.obj -> /home/algo/Documents/print_factory_type/data/991112/875986_305425_P88136_9cm_x1.obj
下载文件: objs/download/print/305425/base/model/9cm/305425Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/875986_305425Tex1.jpg
下载文件: objs/download/print/305425/base/model/9cm/blend/305425_org.blend -> /home/algo/Documents/print_factory_type/data/991112/blend/305425_org.blend
下载 in obj_key
下载 in obj_key
开始下载print_model_info: BatchModelInfo(order_id=857420, pid=268473, print_order_id=85240, model_size='5cm_x1', path='objs/download/print/268473/base_cartoon/badge/1/5cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/268473/base_cartoon/badge/1/5cm/' 存在,共找到 4 个对象。
已更新材质库引用: 268473Tex1.jpg -> 857420_268473Tex1.jpg
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.mtl -> /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.obj -> /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
下载 in obj_key
下载耗时:5.1790547370910645
selected_machine 小机型 selected_mode 紧凑 output_format JSON
make_bbox_for_print total_time=5.503540992736816
z_volume_center1=3.494785873362541, z_volume_center2=4.630428442012234
compute_bbox 857420_268473_P85240_5cm_x1.obj time=2.4211812019348145
z_volume_center1=7.4748487042688625, z_volume_center2=8.653334486695568
compute_bbox 875986_305425_P88136_9cm_x1.obj time=2.196851968765259
all_models [{'name': '857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply', 'dimensions': (50, 50, 9)}, {'name': '875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply', 'dimensions': (90, 25, 19)}]
开始计算排序...
开始计算排序...
单层放置模式:所有模型只能放在平台底面(Z=0)
arrange_models 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply
pre_name1=875986_305425_P88136_9cm, pre_name2=
First Model 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply
model position1 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply (96, 339, 0)
arrange_models 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply
pre_name1=857420_268473_P85240_5cm, pre_name2=
857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply px 150 96 90
final_y2 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply 56 345 50 6 150
can_place False 1 cross_border 150, 340, 0, {'name': '857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply', 'dimensions': (50, 50, 9), 'first_line': True}, 380, 345, 250
model position2 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply (150, 339, 0)
Placed Models:
- 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply at (96, 339, 0) with dimensions (90, 25, 19)
- 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply at (150, 339, 0) with dimensions (50, 50, 9)
Unplaced Models:
- 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply at (96, 339, 0) with dimensions (90, 25, 19)
- 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply at (150, 339, 0) with dimensions (50, 50, 9)
pcd_second : len(pcd_first)=2, len(pcd_second)=0, len(last_pcd_processed)=2, len(pcd_all)=2
is_test= True
need_upload_result=False
3D打印布局已保存至: /home/algo/Documents/print_factory_type/data/991112/991112.json
排版错误模型数量::0
排版剩余模型数量::0
排版完成
排版总耗时::0 分 / 16.58465886116028 秒
计算重心::0 分 / 16.583354234695435 秒
排包围盒::0 分 / 0.0005736351013183594 秒
挪紧凑::0 分 / 8.440017700195312e-05 秒
移动到位置::0 分 / 0.0006456375122070312 秒
选择机型=小机型
已加载并变换: 857420_268473_P85240_5cm_x1.obj
已加载并变换: 875986_305425_P88136_9cm_x1.obj
add_plank /home/algo/Documents/print_factory_type/blank/blank_bias/blank_small.obj
成功加载并变换了 3 个模型
高级渲染图片已保存到: /home/algo/Documents/print_factory_type/data/991112/991112.jpg
保存截图耗时::0 分 / 6.980211019515991 秒
执行上 传-parent_dir=/home/algo/Documents/print_factory_type,base_original_obj_dir=/home/algo/Documents/print_factory_type/data/991112,batch_id=991112
is_use_debug_oss=True
target_dir=/home/algo/Documents/print_factory_type/data/991112, batch_id=991112
文件已上传到: batchPrint/debug_hsc/991112/991112.json
文件已上传到: batchPrint/debug_hsc/991112/991112.jpg
is_test=True
need_upload_result=False
is_run_local_data=True
pre_batch_id=991112
print_ids=[85240, 88136]
----------------------------------------
----------------------------------------
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425Tex1.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425_P88136_9cm_x1.obj
已删除: /home/algo/Documents/print_factory_type/data/991112/blend/305425_org.blend
目录 /home/algo/Documents/print_factory_type/data/991112/blend 下的内容已清空。
已删除子目录: /home/algo/Documents/print_factory_type/data/991112/blend
已删除: /home/algo/Documents/print_factory_type/data/991112/991112.json
已删除: /home/algo/Documents/print_factory_type/data/991112/991112.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425.mtl
目录 /home/algo/Documents/print_factory_type/data/991112 下的内容已清空。
已删除子目录: /home/algo/Documents/print_factory_type/data/991112
目录 /home/algo/Documents/print_factory_type/data/ 下的内容已清空。
目录 /home/algo/Documents/print_factory_type/full/ 下的内容已清空。
从文件读取了 2 个path
开始下载print_model_info: BatchModelInfo(order_id=875986, pid=305425, print_order_id=88136, model_size='9cm_x1', path='objs/download/print/305425/base/model/9cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/305425/base/model/9cm/' 存在,共找到 6 个对象。
已更新材质库引用: 305425Tex1.jpg -> 875986_305425Tex1.jpg
下载文件: objs/download/print/305425/base/model/9cm/305425.mtl -> /home/algo/Documents/print_factory_type/data/991112/875986_305425.mtl
下载文件: objs/download/print/305425/base/model/9cm/305425.obj -> /home/algo/Documents/print_factory_type/data/991112/875986_305425_P88136_9cm_x1.obj
下载文件: objs/download/print/305425/base/model/9cm/305425Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/875986_305425Tex1.jpg
下载文件: objs/download/print/305425/base/model/9cm/blend/305425_org.blend -> /home/algo/Documents/print_factory_type/data/991112/blend/305425_org.blend
下载 in obj_key
下载 in obj_key
开始下载print_model_info: BatchModelInfo(order_id=857420, pid=268473, print_order_id=85240, model_size='5cm_x1', path='objs/download/print/268473/base_cartoon/badge/1/5cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/268473/base_cartoon/badge/1/5cm/' 存在,共找到 4 个对象。
已更新材质库引用: 268473Tex1.jpg -> 857420_268473Tex1.jpg
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.mtl -> /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.obj -> /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
下载 in obj_key
下载耗时:4.482503890991211
selected_machine 小机型 selected_mode 紧凑 output_format JSON
make_bbox_for_print total_time=5.583776950836182
z_volume_center1=3.494785873362541, z_volume_center2=4.630428442012234
compute_bbox 857420_268473_P85240_5cm_x1.obj time=2.4529471397399902
z_volume_center1=7.4748487042688625, z_volume_center2=8.653334486695568
compute_bbox 875986_305425_P88136_9cm_x1.obj time=2.36457896232605
all_models [{'name': '857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply', 'dimensions': (50, 50, 9)}, {'name': '875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply', 'dimensions': (90, 25, 19)}]
开始计算排序...
开始计算排序...
单层放置模式:所有模型只能放在平台底面(Z=0)
arrange_models 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply
pre_name1=875986_305425_P88136_9cm, pre_name2=
First Model 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply
model position1 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply (96, 339, 0)
arrange_models 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply
pre_name1=857420_268473_P85240_5cm, pre_name2=
857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply px 150 96 90
final_y2 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply 56 345 50 6 150
can_place False 1 cross_border 150, 340, 0, {'name': '857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply', 'dimensions': (50, 50, 9), 'first_line': True}, 380, 345, 250
model position2 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply (150, 339, 0)
Placed Models:
- 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply at (96, 339, 0) with dimensions (90, 25, 19)
- 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply at (150, 339, 0) with dimensions (50, 50, 9)
Unplaced Models:
- 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply at (96, 339, 0) with dimensions (90, 25, 19)
- 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply at (150, 339, 0) with dimensions (50, 50, 9)
pcd_second : len(pcd_first)=2, len(pcd_second)=0, len(last_pcd_processed)=2, len(pcd_all)=2
is_test= True
need_upload_result=False
3D打印布局已保存至: /home/algo/Documents/print_factory_type/data/991112/991112.json
排版错误模型数量::0
排版剩余模型数量::0
排版完成
排版总耗时::0 分 / 17.15925693511963 秒
计算重心::0 分 / 17.15758728981018 秒
排包围盒::0 分 / 0.0007126331329345703 秒
挪紧凑::0 分 / 0.00011324882507324219 秒
移动到位置::0 分 / 0.0008420944213867188 秒
选择机型=小机型
已加载并变换: 857420_268473_P85240_5cm_x1.obj
已加载并变换: 875986_305425_P88136_9cm_x1.obj
add_plank /home/algo/Documents/print_factory_type/blank/blank_bias/blank_small.obj
成功加载并变换了 3 个模型
高级渲染图片已保存到: /home/algo/Documents/print_factory_type/data/991112/991112.jpg
保存截图耗时::0 分 / 7.824073791503906 秒
执行上 传-parent_dir=/home/algo/Documents/print_factory_type,base_original_obj_dir=/home/algo/Documents/print_factory_type/data/991112,batch_id=991112
is_use_debug_oss=True
target_dir=/home/algo/Documents/print_factory_type/data/991112, batch_id=991112
文件已上传到: batchPrint/debug_hsc/991112/991112.json
文件已上传到: batchPrint/debug_hsc/991112/991112.jpg
is_test=True
need_upload_result=False
is_run_local_data=True
pre_batch_id=991112
print_ids=[85240, 88136]
----------------------------------------
----------------------------------------
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425Tex1.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425_P88136_9cm_x1.obj
已删除: /home/algo/Documents/print_factory_type/data/991112/blend/305425_org.blend
目录 /home/algo/Documents/print_factory_type/data/991112/blend 下的内容已清空。
已删除子目录: /home/algo/Documents/print_factory_type/data/991112/blend
已删除: /home/algo/Documents/print_factory_type/data/991112/991112.json
已删除: /home/algo/Documents/print_factory_type/data/991112/991112.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425.mtl
目录 /home/algo/Documents/print_factory_type/data/991112 下的内容已清空。
已删除子目录: /home/algo/Documents/print_factory_type/data/991112
目录 /home/algo/Documents/print_factory_type/data/ 下的内容已清空。
目录 /home/algo/Documents/print_factory_type/full/ 下的内容已清空。
从文件读取了 2 个path
开始下载print_model_info: BatchModelInfo(order_id=875986, pid=305425, print_order_id=88136, model_size='9cm_x1', path='objs/download/print/305425/base/model/9cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/305425/base/model/9cm/' 存在,共找到 6 个对象。
已更新材质库引用: 305425Tex1.jpg -> 875986_305425Tex1.jpg
下载文件: objs/download/print/305425/base/model/9cm/305425.mtl -> /home/algo/Documents/print_factory_type/data/991112/875986_305425.mtl
下载文件: objs/download/print/305425/base/model/9cm/305425.obj -> /home/algo/Documents/print_factory_type/data/991112/875986_305425_P88136_9cm_x1.obj
下载文件: objs/download/print/305425/base/model/9cm/305425Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/875986_305425Tex1.jpg
下载文件: objs/download/print/305425/base/model/9cm/blend/305425_org.blend -> /home/algo/Documents/print_factory_type/data/991112/blend/305425_org.blend
下载 in obj_key
下载 in obj_key
开始下载print_model_info: BatchModelInfo(order_id=857420, pid=268473, print_order_id=85240, model_size='5cm_x1', path='objs/download/print/268473/base_cartoon/badge/1/5cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/268473/base_cartoon/badge/1/5cm/' 存在,共找到 4 个对象。
已更新材质库引用: 268473Tex1.jpg -> 857420_268473Tex1.jpg
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.mtl -> /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.obj -> /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
下载 in obj_key
下载耗时:4.554089784622192
selected_machine 小机型 selected_mode 紧凑 output_format JSON
make_bbox_for_print total_time=5.638967514038086
z_volume_center1=3.494785873362541, z_volume_center2=4.630428442012234
compute_bbox 857420_268473_P85240_5cm_x1.obj time=2.3993477821350098
z_volume_center1=7.4748487042688625, z_volume_center2=8.653334486695568
compute_bbox 875986_305425_P88136_9cm_x1.obj time=2.222135305404663
all_models [{'name': '857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply', 'dimensions': (50, 50, 9)}, {'name': '875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply', 'dimensions': (90, 25, 19)}]
开始计算排序...
开始计算排序...
单层放置模式:所有模型只能放在平台底面(Z=0)
arrange_models 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply
pre_name1=875986_305425_P88136_9cm, pre_name2=
First Model 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply
model position1 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply (96, 339, 0)
arrange_models 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply
pre_name1=857420_268473_P85240_5cm, pre_name2=
857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply px 150 96 90
final_y2 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply 56 345 50 6 150
can_place False 1 cross_border 150, 340, 0, {'name': '857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply', 'dimensions': (50, 50, 9), 'first_line': True}, 380, 345, 250
model position2 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply (150, 339, 0)
Placed Models:
- 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply at (96, 339, 0) with dimensions (90, 25, 19)
- 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply at (150, 339, 0) with dimensions (50, 50, 9)
Unplaced Models:
- 875986_305425_P88136_9cm_x1=19.759+25.308+90.184.ply at (96, 339, 0) with dimensions (90, 25, 19)
- 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply at (150, 339, 0) with dimensions (50, 50, 9)
pcd_second : len(pcd_first)=2, len(pcd_second)=0, len(last_pcd_processed)=2, len(pcd_all)=2
is_test= True
need_upload_result=False
3D打印布局已保存至: /home/algo/Documents/print_factory_type/data/991112/991112.json
排版错误模型数量::0
排版剩余模型数量::0
排版完成
排版总耗时::0 分 / 16.85209321975708 秒
计算重心::0 分 / 16.850478410720825 秒
排包围盒::0 分 / 0.0006732940673828125 秒
挪紧凑::0 分 / 0.00010204315185546875 秒
移动到位置::0 分 / 0.0008370876312255859 秒
选择机型=小机型
已加载并变换: 857420_268473_P85240_5cm_x1.obj
已加载并变换: 875986_305425_P88136_9cm_x1.obj
add_plank /home/algo/Documents/print_factory_type/blank/blank_bias/blank_small.obj
成功加载并变换了 3 个模型
高级渲染图片已保存到: /home/algo/Documents/print_factory_type/data/991112/991112.jpg
保存截图耗时::0 分 / 7.028083324432373 秒
执行上 传-parent_dir=/home/algo/Documents/print_factory_type,base_original_obj_dir=/home/algo/Documents/print_factory_type/data/991112,batch_id=991112
is_use_debug_oss=True
target_dir=/home/algo/Documents/print_factory_type/data/991112, batch_id=991112
文件已上传到: batchPrint/debug_hsc/991112/991112.json
文件已上传到: batchPrint/debug_hsc/991112/991112.jpg
is_test=True
need_upload_result=False
is_run_local_data=True
pre_batch_id=991112
print_ids=[85240, 88136]
----------------------------------------
----------------------------------------
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425Tex1.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425_P88136_9cm_x1.obj
已删除: /home/algo/Documents/print_factory_type/data/991112/blend/305425_org.blend
目录 /home/algo/Documents/print_factory_type/data/991112/blend 下的内容已清空。
已删除子目录: /home/algo/Documents/print_factory_type/data/991112/blend
已删除: /home/algo/Documents/print_factory_type/data/991112/991112.json
已删除: /home/algo/Documents/print_factory_type/data/991112/991112.jpg
已删除: /home/algo/Documents/print_factory_type/data/991112/875986_305425.mtl
目录 /home/algo/Documents/print_factory_type/data/991112 下的内容已清空。
已删除子目录: /home/algo/Documents/print_factory_type/data/991112
目录 /home/algo/Documents/print_factory_type/data/ 下的内容已清空。
目录 /home/algo/Documents/print_factory_type/full/ 下的内容已清空。
从文件读取了 2 个path
开始下载print_model_info: BatchModelInfo(order_id=857420, pid=268473, print_order_id=85240, model_size='5cm_x1', path='objs/download/print/268473/base_cartoon/badge/1/5cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/268473/base_cartoon/badge/1/5cm/' 存在,共找到 4 个对象。
已更新材质库引用: 268473Tex1.jpg -> 857420_268473Tex1.jpg
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.mtl -> /home/algo/Documents/print_factory_type/data/991112/857420_268473.mtl
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473.obj -> /home/algo/Documents/print_factory_type/data/991112/857420_268473_P85240_5cm_x1.obj
下载文件: objs/download/print/268473/base_cartoon/badge/1/5cm/268473Tex1.jpg -> /home/algo/Documents/print_factory_type/data/991112/857420_268473Tex1.jpg
下载 in obj_key
开始下载print_model_info: BatchModelInfo(order_id=875986, pid=305425, print_order_id=88136, model_size='9cm_x1', path='objs/download/print/305425/base/model/9cm', count=1)
target_dir= /home/algo/Documents/print_factory_type/data/991112
前缀 'objs/download/print/305425/base/model/9cm/' 存在,共找到 6 个对象。

1567
point_cloud_layout.py

File diff suppressed because it is too large Load Diff

174
print_factory_type_setting_obj_run.py

@ -7,12 +7,21 @@ import argparse @@ -7,12 +7,21 @@ import argparse
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)
from print_show_weight_max_obj import make_bbox_for_print,copy_obj_2x
from print_mplot3d_point_cloud_layout import *
from print_merged_many_obj import move_compact_obj_to_file
from print_show_weight_max_obj import copy_obj_2x
from point_cloud_layout import *
from test_load_json import load_show_save
from download_print import upload_result
from config import print_factory_type_dir
from config import is_test
from config import url_send_layout
from config import big_machine_size
from config import small_machine_size
from general import need_upload_result
from point_cloud_layout import make_bbox_for_print
def get_base_directory():
"""获取脚本或可执行文件的基础目录"""
if getattr(sys, 'frozen', False):
@ -26,116 +35,63 @@ def get_base_directory(): @@ -26,116 +35,63 @@ def get_base_directory():
from datetime import datetime
import gc
def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=None,batch_id=0,show_chart=True,selected_mode="标准",output_format="JSON",selected_machine="大机型"):
def print_type_setting_obj(base_original_obj_dir=None,batch_id=0,selected_mode="标准",output_format="JSON",selected_machine="大机型"):
print_start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
weight_fix_out_obj_dir = f"{cache_type_setting_dir}/temp/print_weight_fix_data_obj"
weight_fix_out_ply_dir = f"{cache_type_setting_dir}/temp/print_weight_fix_data_ply"
bounds_fix_out_dir = f"{cache_type_setting_dir}/temp/print_bounds_fix_data"
bounds_compact_out_dir = f"{cache_type_setting_dir}/temp/print_bounds_compact_data"
compact_obj_out_dir = f"{cache_type_setting_dir}/temp/print_compact_obj" # 最后的结果
placed_remove_dir = f"{base_original_obj_dir}/place_remove_dir" # 已经放置的放到这个目录
# 获取基础目录
base_path = get_base_directory()
# 获取父目录
parent_dir = os.path.dirname(base_path)
bad_dir = os.path.join(parent_dir, "bad")
full_dir = os.path.join(parent_dir, "full")
# print(bad_dir)
# print(full_dir)
if output_format == "模型":
if os.path.exists(weight_fix_out_obj_dir):
shutil.rmtree(weight_fix_out_obj_dir)
if os.path.exists(weight_fix_out_ply_dir):
shutil.rmtree(weight_fix_out_ply_dir)
if os.path.exists(bounds_fix_out_dir):
shutil.rmtree(bounds_fix_out_dir)
if os.path.exists(bounds_compact_out_dir):
shutil.rmtree(bounds_compact_out_dir)
if os.path.exists(compact_obj_out_dir):
shutil.rmtree(compact_obj_out_dir)
# if os.path.exists(output_folder): # 不需要
# shutil.rmtree(output_folder)
time.sleep(1)
if not os.path.exists(weight_fix_out_obj_dir):
os.makedirs(weight_fix_out_obj_dir)
if not os.path.exists(weight_fix_out_ply_dir):
os.makedirs(weight_fix_out_ply_dir)
if not os.path.exists(bounds_fix_out_dir):
os.mkdir(bounds_fix_out_dir)
if not os.path.exists(bounds_compact_out_dir):
os.makedirs(bounds_compact_out_dir)
if not os.path.exists(compact_obj_out_dir):
os.makedirs(compact_obj_out_dir)
# if not os.path.exists(output_folder): # 不需要
# os.makedirs(output_folder)
print("selected_machine",selected_machine,"selected_mode",selected_mode,"output_format",output_format)
compact_min_dis = True
compact_min_dis2 = False
if selected_mode=="标准" :
compact_min_dis = False
# if output_format=="JSON":
compact_min_dis2 = True
else :
compact_min_dis = True
move_back = True
machine_size = [600, 500, 300]
machine_size = big_machine_size
if selected_machine=="小机型":
machine_size[0] = 380
machine_size[1] = 345
machine_size[2] = 250
machine_size = small_machine_size
start_time = time.time()
copy_obj_2x(base_original_obj_dir)
dict_bad = {}
dict_best_angel = {}
dict_fix = {}
dict_origin = {}
dict_origin_real = {}
dict_total_matrix= {}
dict_mesh_obj = make_bbox_for_print(base_original_obj_dir, weight_fix_out_obj_dir, weight_fix_out_ply_dir,show_chart,dict_bad, dict_best_angel,dict_fix,dict_origin,dict_origin_real,compact_min_dis or compact_min_dis2,dict_total_matrix)
mesh_count = len(dict_mesh_obj)
dict_total_matrix, all_models, dict_pcd_fix = make_bbox_for_print(base_original_obj_dir,dict_bad,dict_origin,compact_min_dis)
mesh_count = len(dict_origin)
if mesh_count<=0:
print("选择的文件夹没有模型")
return -1
end_time1 = time.time()
dict_bounds_fix = {}
placed_models= ply_print_layout_platform(weight_fix_out_obj_dir,weight_fix_out_ply_dir,bounds_fix_out_dir,show_chart,dict_mesh_obj,dict_fix,dict_bounds_fix,machine_size,dict_total_matrix)
###以上迁移到部分前置计算
dict_pcd_fix2 = {}
list_placed_model = ply_print_layout_platform(dict_pcd_fix,dict_pcd_fix2,machine_size,dict_total_matrix,all_models)
end_time2 = time.time()
dict_unplaced = {}
dict_compact = {}
dict_mesh_obj = dict_origin
if compact_min_dis:
# if output_format=="JSON" :
if True :
can_compact_json = True
if can_compact_json :
compact_mode_for_min_dis1_json(bounds_fix_out_dir,bounds_compact_out_dir,show_chart,move_back,placed_models,dict_unplaced,dict_bounds_fix,dict_compact,machine_size,dict_total_matrix)
compact_mode_for_min_dis_json(list_placed_model,dict_unplaced,dict_pcd_fix,machine_size,dict_total_matrix)
else:
pass_for_min_dis(bounds_fix_out_dir, bounds_compact_out_dir,placed_models, dict_unplaced,dict_bounds_fix,dict_compact)
else :
compact_mode_for_min_dis(bounds_fix_out_dir,bounds_compact_out_dir,show_chart,move_back,placed_models,dict_unplaced,dict_bounds_fix,dict_compact,machine_size)
else:
compact_min_dis2 = False
if compact_min_dis2:
compact_mode_for_min_dis2_json(bounds_fix_out_dir,bounds_compact_out_dir,show_chart,move_back,placed_models,dict_unplaced,dict_bounds_fix,dict_compact,machine_size,dict_total_matrix)
else :
pass_for_min_dis(bounds_fix_out_dir, bounds_compact_out_dir,placed_models, dict_unplaced,dict_bounds_fix,dict_compact)
pass_for_min_dis(list_placed_model, dict_unplaced,dict_pcd_fix)
end_time3 = time.time()
#if os.path.exists(placed_remove_dir):
# shutil.rmtree(placed_remove_dir)
if not os.path.exists(placed_remove_dir):
os.makedirs(placed_remove_dir)
if not os.path.exists(bad_dir):
os.makedirs(bad_dir)
@ -143,23 +99,14 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non @@ -143,23 +99,14 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non
os.makedirs(full_dir)
is_small_machine = True if selected_machine=="小机型" else False
use_json = True if output_format=="JSON" else False
save_mesh = True if output_format=="模型" else False
# if use_json:
version = "print_type_setting25.local"
layout_data, send_layout_data = move_obj_to_compact_bounds_json(bounds_fix_out_dir,bounds_compact_out_dir,weight_fix_out_obj_dir,
base_original_obj_dir,compact_obj_out_dir,dict_mesh_obj,dict_unplaced,
placed_remove_dir,dict_bad,bad_dir,full_dir,dict_best_angel,dict_bounds_fix,
dict_compact,dict_origin,dict_total_matrix,save_mesh,cache_type_setting_dir,
batch_id, print_start_time,selected_machine,selected_mode,version)
# else:
# move_obj_to_compact_bounds(bounds_fix_out_dir,bounds_compact_out_dir,weight_fix_out_obj_dir,base_original_obj_dir,compact_obj_out_dir,dict_mesh_obj,dict_unplaced,placed_remove_dir,dict_bad,bad_dir,full_dir,dict_bounds_fix,dict_compact,dict_origin)
send_layout_data = move_obj_to_compact_bounds_json(base_original_obj_dir,dict_mesh_obj,dict_unplaced,dict_bad,bad_dir,full_dir,dict_pcd_fix,
dict_total_matrix,save_mesh,batch_id,print_start_time,selected_machine,selected_mode,version)
end_time4 = time.time()
#move_compact_obj_to_file(compact_obj_out_dir, output_folder) # 不需要
print("排版完成")
end_time = time.time()
elapsed_seconds = end_time - start_time
@ -179,26 +126,12 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non @@ -179,26 +126,12 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non
print(f"挪紧凑::{elapsed_minutes3} 分 / {elapsed_seconds3}")
print(f"移动到位置::{elapsed_minutes4} 分 / {elapsed_seconds4}")
dict_mesh_obj.clear()
del dict_mesh_obj
dict_bad.clear()
del dict_bad
dict_fix.clear()
del dict_fix
dict_bounds_fix.clear()
del dict_bounds_fix
dict_unplaced.clear()
del dict_unplaced
dict_compact.clear()
del dict_compact
gc.collect()
# print(base_original_obj_dir,blank_dir,batch_id)
is_screenshot = True
if is_screenshot:
start_time = time.time()
blank_path = get_blank_path(parent_dir, is_small_machine)
blank_path = get_blank_path(is_small_machine)
load_show_save(base_original_obj_dir, dict_origin, blank_path, batch_id)
elapsed_seconds5 = time.time() - start_time
elapsed_minutes5 = int(elapsed_seconds5 // 60)
@ -210,23 +143,24 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non @@ -210,23 +143,24 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non
is_upload_result = True
if is_upload_result:
print(f"执行上传-parent_dir={parent_dir},base_original_obj_dir={base_original_obj_dir},batch_id={batch_id}")
print(f"执行上 传-parent_dir={print_factory_type_dir},base_original_obj_dir={base_original_obj_dir},batch_id={batch_id}")
# oss_config = f"{base_original_obj_dir}/../print_factory_type_setting_big/download_print/run.yaml"
oss_config = f"{parent_dir}/print_factory_type_setting_big/download_print/run.yaml"
oss_config = f"{print_factory_type_dir}/print_factory_type_setting_big/download_print/run.yaml"
upload_result(base_original_obj_dir, oss_config, batch_id)
print(f"is_test={is_test}")
print(f"need_upload_result={need_upload_result()}")
# if need_upload_result():
if is_test :
is_send_layout_data = False
else :
is_send_layout_data = True
# is_send_layout_data = False
if is_send_layout_data:
print(f"send_layout_data={send_layout_data}")
url = 'https://mp.api.suwa3d.com/api/printTypeSettingOrder/printTypeSettingOrderSuccess'
# url = 'http://127.0.0.1:8199/api/typeSettingPrintOrder/printTypeSettingOrderSuccess'
# url = 'https://mp.api.suwa3d.com/api/printTypeSettingOrder/printTypeSettingOrderSuccess'
url = url_send_layout
try:
response = requests.post(url, json.dumps(send_layout_data), timeout=30)
#写入文件中 log/request.txt
@ -254,38 +188,46 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non @@ -254,38 +188,46 @@ def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=Non
return 0
def get_blank_path(parent_dir=None, is_small_machine=False):
def get_blank_path(is_small_machine=False):
if is_small_machine:
return os.path.join(parent_dir, "blank/blank_bias/blank_small.obj")
return os.path.join(print_factory_type_dir, "blank/blank_bias/blank_small.obj")
else:
return os.path.join(parent_dir, "blank/blank_bias/blank2.obj")
return os.path.join(print_factory_type_dir, "blank/blank_bias/blank2.obj")
def preview(base_original_obj_dir=None, batch_id=0, is_small_machine=False):
base_path = get_base_directory()
parent_dir = os.path.dirname(base_path)
# blank_dir = os.path.join(parent_dir, "blank", "blank_bias")
blank_path = get_blank_path(parent_dir, is_small_machine)
blank_path = get_blank_path(is_small_machine)
load_show_save(base_original_obj_dir, {}, blank_path, batch_id, True)
if __name__ == '__main__':
def get_pcd(obj, is_down_sample=True):
vertices = np.asarray(obj.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
if is_down_sample:
voxel_size = 3
pcd_downsampled = down_sample(pcd, voxel_size, False)
return pcd_downsampled
else:
return pcd
if __name__ == '__main__':
# parser = argparse.ArgumentParser()
# parser.add_argument("--batch_id", type=str, required=True, help="batch_id")
# args = parser.parse_args()
# batch_id = args.batch_id
batch_id = "9"
batch_id = "1"
src_dir = batch_id
selected_mode="紧凑" # 标准 紧凑
output_format="JSON" # 模型 JSON
selected_machine = "大机型" # 小机型 大机型
print_factory_type_dir="/root/print_factory_type"
# cache_type_setting_dir=f"/data/datasets_20t/type_setting_test_data/{src_dir}"
cache_type_setting_dir=f"{print_factory_type_dir}/{src_dir}/arrange"
base_original_obj_dir = f"{print_factory_type_dir}/{src_dir}"
print_type_setting_obj(base_original_obj_dir=base_original_obj_dir,cache_type_setting_dir=cache_type_setting_dir,
batch_id=batch_id,show_chart=False,selected_mode=selected_mode,output_format=output_format,selected_machine=selected_machine)
print_type_setting_obj(base_original_obj_dir=base_original_obj_dir,
batch_id=batch_id,selected_mode=selected_mode,output_format=output_format,selected_machine=selected_machine)

193
print_factory_type_setting_obj_run_GUI.py

@ -1,193 +0,0 @@ @@ -1,193 +0,0 @@
import os
import shutil
import time
import sys
script_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, script_dir)
from print_show_weight_max_obj import make_bbox_for_print,copy_obj_2x
from print_mplot3d_point_cloud_layout import *
from print_merged_many_obj import move_compact_obj_to_file
from test_load_json import load_and_show
def get_base_directory():
"""获取脚本或可执行文件的基础目录"""
if getattr(sys, 'frozen', False):
# 打包后的可执行文件环境
base_path = os.path.dirname(sys.executable)
else:
# 正常脚本运行环境
base_path = os.path.dirname(os.path.abspath(__file__))
return base_path
from datetime import datetime
def print_type_setting_obj(base_original_obj_dir=None,cache_type_setting_dir=None,show_chart=True,selected_mode="标准",output_format="JSON",selected_machine="大机型"):
print_start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
weight_fix_out_obj_dir = f"{cache_type_setting_dir}/print_weight_fix_data_obj"
weight_fix_out_ply_dir = f"{cache_type_setting_dir}/print_weight_fix_data_ply"
bounds_fix_out_dir = f"{cache_type_setting_dir}/print_bounds_fix_data"
bounds_compact_out_dir = f"{cache_type_setting_dir}/print_bounds_compact_data"
compact_obj_out_dir = f"{cache_type_setting_dir}/print_compact_obj" # 最后的结果
placed_remove_dir = f"{base_original_obj_dir}/place_remove_dir" # 已经放置的放到这个目录
# 获取基础目录
base_path = get_base_directory()
# 获取父目录
parent_dir = os.path.dirname(base_path)
bad_dir = os.path.join(parent_dir, "bad")
full_dir = os.path.join(parent_dir, "full")
blank_dir = os.path.join(parent_dir, "blank")
# print(bad_dir)
# print(full_dir)
# 测试代码
"""
selected_machine = "小机型" # 小机型 大机型
selected_mode="紧凑" # 标准 紧凑
output_format="模型" # 模型 JSON
"""
if output_format == "模型":
if os.path.exists(weight_fix_out_obj_dir):
shutil.rmtree(weight_fix_out_obj_dir)
if os.path.exists(weight_fix_out_ply_dir):
shutil.rmtree(weight_fix_out_ply_dir)
if os.path.exists(bounds_fix_out_dir):
shutil.rmtree(bounds_fix_out_dir)
if os.path.exists(bounds_compact_out_dir):
shutil.rmtree(bounds_compact_out_dir)
if os.path.exists(compact_obj_out_dir):
shutil.rmtree(compact_obj_out_dir)
# if os.path.exists(output_folder): # 不需要
# shutil.rmtree(output_folder)
time.sleep(1)
if not os.path.exists(weight_fix_out_obj_dir):
os.makedirs(weight_fix_out_obj_dir)
if not os.path.exists(weight_fix_out_ply_dir):
os.makedirs(weight_fix_out_ply_dir)
if not os.path.exists(bounds_fix_out_dir):
os.mkdir(bounds_fix_out_dir)
if not os.path.exists(bounds_compact_out_dir):
os.makedirs(bounds_compact_out_dir)
if not os.path.exists(compact_obj_out_dir):
os.makedirs(compact_obj_out_dir)
# if not os.path.exists(output_folder): # 不需要
# os.makedirs(output_folder)
print("selected_machine",selected_machine,"selected_mode",selected_mode,"output_format",output_format)
compact_min_dis = True
compact_min_dis2 = False
if selected_mode=="标准" :
compact_min_dis = False
if output_format=="JSON":
compact_min_dis2 = True
else :
compact_min_dis = True
move_back = True
machine_size = [600, 500, 300]
if selected_machine=="小机型":
machine_size[0] = 380
machine_size[1] = 345
machine_size[2] = 250
start_time = time.time()
copy_obj_2x(base_original_obj_dir)
dict_bad = {}
dict_best_angel = {}
dict_fix = {}
dict_origin = {}
dict_total_matrix= {}
dict_mesh_obj = make_bbox_for_print(base_original_obj_dir, weight_fix_out_obj_dir, weight_fix_out_ply_dir,show_chart,dict_bad, dict_best_angel,dict_fix,dict_origin,compact_min_dis or compact_min_dis2,dict_total_matrix)
mesh_count = len(dict_mesh_obj)
if mesh_count<=0:
print("选择的文件夹没有模型")
return -1
end_time1 = time.time()
dict_bounds_fix = {}
placed_models= ply_print_layout_platform(weight_fix_out_obj_dir,weight_fix_out_ply_dir,bounds_fix_out_dir,show_chart,dict_mesh_obj,dict_fix,dict_bounds_fix,machine_size,dict_total_matrix)
end_time2 = time.time()
dict_unplaced = {}
dict_compact = {}
if compact_min_dis:
if output_format=="JSON" :
can_compact_json = True
if can_compact_json :
compact_mode_for_min_dis1_json(bounds_fix_out_dir,bounds_compact_out_dir,show_chart,move_back,placed_models,dict_unplaced,dict_bounds_fix,dict_compact,machine_size,dict_total_matrix)
else :
pass_for_min_dis(bounds_fix_out_dir, bounds_compact_out_dir,placed_models, dict_unplaced,dict_bounds_fix,dict_compact)
else :
compact_mode_for_min_dis(bounds_fix_out_dir,bounds_compact_out_dir,show_chart,move_back,placed_models,dict_unplaced,dict_bounds_fix,dict_compact,machine_size)
else:
if compact_min_dis2:
compact_mode_for_min_dis2_json(bounds_fix_out_dir,bounds_compact_out_dir,show_chart,move_back,placed_models,dict_unplaced,dict_bounds_fix,dict_compact,machine_size,dict_total_matrix)
else :
pass_for_min_dis(bounds_fix_out_dir, bounds_compact_out_dir,placed_models, dict_unplaced,dict_bounds_fix,dict_compact)
end_time3 = time.time()
#if os.path.exists(placed_remove_dir):
# shutil.rmtree(placed_remove_dir)
if not os.path.exists(placed_remove_dir):
os.makedirs(placed_remove_dir)
if not os.path.exists(bad_dir):
os.makedirs(bad_dir)
if not os.path.exists(full_dir):
os.makedirs(full_dir)
save_mesh = True if output_format=="模型" else False
# move_obj_to_compact_bounds_json(bounds_fix_out_dir,bounds_compact_out_dir,weight_fix_out_obj_dir,base_original_obj_dir,compact_obj_out_dir,dict_mesh_obj,dict_unplaced,placed_remove_dir,dict_bad,bad_dir,full_dir,dict_best_angel,dict_bounds_fix,dict_compact,save_mesh,dict_origin,dict_total_matrix,print_start_time)
version = "print_type_setting25.11.21.1"
batch_id = os.path.basename(base_path.rstrip('/'))
move_obj_to_compact_bounds_json(bounds_fix_out_dir,bounds_compact_out_dir,weight_fix_out_obj_dir,
base_original_obj_dir,compact_obj_out_dir,dict_mesh_obj,dict_unplaced,
placed_remove_dir,dict_bad,bad_dir,full_dir,dict_best_angel,dict_bounds_fix,
dict_compact,dict_origin,dict_total_matrix,save_mesh,cache_type_setting_dir,
batch_id, print_start_time,selected_machine,selected_mode,version)
end_time4 = time.time()
#move_compact_obj_to_file(compact_obj_out_dir, output_folder) # 不需要
print("排版完成")
end_time = time.time()
elapsed_seconds = end_time - start_time
elapsed_seconds1 = end_time1 - start_time # 计算重心
elapsed_seconds2 = end_time2 - end_time1 #
elapsed_seconds3 = end_time3 - end_time2
elapsed_seconds4 = end_time4 - end_time3
elapsed_minutes = int(elapsed_seconds // 60)
elapsed_minutes1 = int(elapsed_seconds1 // 60)
elapsed_minutes2 = int(elapsed_seconds2 // 60)
elapsed_minutes3 = int(elapsed_seconds3 // 60)
elapsed_minutes4 = int(elapsed_seconds4 // 60)
print(f"排版总耗时::{elapsed_minutes} 分 / {elapsed_seconds}")
print(f"计算重心::{elapsed_minutes1} 分 / {elapsed_seconds1}")
print(f"排包围盒::{elapsed_minutes2} 分 / {elapsed_seconds2}")
print(f"挪紧凑::{elapsed_minutes3} 分 / {elapsed_seconds3}")
print(f"移动到位置::{elapsed_minutes4} 分 / {elapsed_seconds4}")
# load_and_show(base_original_obj_dir,blank_dir)
return 0
def preview(base_original_obj_dir=None):
base_path = get_base_directory()
parent_dir = os.path.dirname(base_path)
blank_dir = os.path.join(parent_dir, "blank", "blank_bias")
load_and_show(base_original_obj_dir,blank_dir)
if __name__ == '__main__':
src_dir = "12-9" # 1 5.6.5 5.6.4 5.6.1 5.9 temp
cache_type_setting_dir=f"/data/datasets_20t/type_setting_test_data/{src_dir}"
base_original_obj_dir = f"{print_factory_type_dir}/{src_dir}"
print_type_setting_obj(base_original_obj_dir=base_original_obj_dir,cache_type_setting_dir=cache_type_setting_dir,show_chart=False)

162
print_merged_many_obj.py

@ -1,162 +0,0 @@ @@ -1,162 +0,0 @@
import os
import shutil
import time
def merged_obj_for_group(input_folder,output_folder):
""""""
group_size = 5
obj_pid_list = os.listdir(input_folder)
group_obj_list = [obj_pid_list[i:i + group_size] for i in range(0, len(obj_pid_list), group_size)]
print(group_obj_list)
for group_obj in group_obj_list:
print(group_obj)
group_pid = "_".join(group_obj)
print(group_pid)
output_group_folder = os.path.join(output_folder,group_pid)
os.makedirs(output_group_folder, exist_ok=True)
#input_root_folder = "/data/datasets_20t/obj_merger_test_data/"
#output_folder = "/data/datasets_20t/obj_merger_result/"
output_obj = os.path.join(output_group_folder, f"{group_pid}.obj")
output_mtl = os.path.join(output_group_folder, f"{group_pid}.mtl")
# 初始化
merged_obj = []
merged_mtl = []
texture_files = set()
material_offset = 0
vertex_offset = 0
texture_offset = 0
normal_offset = 0
current_materials = {}
# 遍历每个子文件夹
for folder_name in group_obj:
folder_path = os.path.join(input_folder, folder_name)
if not os.path.isdir(folder_path):
continue
obj_file = None
mtl_file = None
texture_file = None
# 寻找 .obj、.mtl 和 .jpg 文件
for file_name in os.listdir(folder_path):
if file_name.endswith(".obj"):
obj_file = os.path.join(folder_path, file_name)
elif file_name.endswith(".mtl"):
mtl_file = os.path.join(folder_path, file_name)
elif file_name.endswith(".jpg"):
texture_file = os.path.join(folder_path, file_name)
# 跳过不完整的文件夹
if not obj_file or not mtl_file or not texture_file:
print(f"跳过不完整的文件夹:{folder_path}")
continue
# 读取 .obj 文件
with open(obj_file, "r") as obj_f:
obj_lines = obj_f.readlines()
for line in obj_lines:
if line.startswith("mtllib"):
# 替换材质文件名
merged_obj.append(f"mtllib {os.path.basename(output_mtl)}\n")
elif line.startswith("usemtl"):
# 重命名材质名称,避免冲突
original_material = line.split()[1]
new_material = f"{original_material}_{material_offset}"
print(f"original_material---{original_material}")
print(f"new_material---{new_material}")
merged_obj.append(f"usemtl {new_material}\n")
current_materials[original_material] = new_material
elif line.startswith("v "): # 顶点
vertex = line.split()[1:]
merged_obj.append(f"v {' '.join(vertex)}\n")
elif line.startswith("vt "): # 纹理坐标
texture = line.split()[1:]
merged_obj.append(f"vt {' '.join(texture)}\n")
elif line.startswith("vn "): # 法线
normal = line.split()[1:]
merged_obj.append(f"vn {' '.join(normal)}\n")
elif line.startswith("f "): # 面数据
face = line.split()[1:]
updated_face = []
for vertex in face:
indices = vertex.split("/")
indices = [
str(int(indices[0]) + vertex_offset) if indices[0] else "",
str(int(indices[1]) + texture_offset) if len(indices) > 1 and indices[1] else "",
str(int(indices[2]) + normal_offset) if len(indices) > 2 and indices[2] else "",
]
updated_face.append("/".join(indices))
merged_obj.append(f"f {' '.join(updated_face)}\n")
# 更新偏移量
vertex_offset += sum(1 for line in obj_lines if line.startswith("v "))
texture_offset += sum(1 for line in obj_lines if line.startswith("vt "))
normal_offset += sum(1 for line in obj_lines if line.startswith("vn "))
# 读取 .mtl 文件
with open(mtl_file, "r") as mtl_f:
mtl_lines = mtl_f.readlines()
for line in mtl_lines:
if line.startswith("newmtl"):
# 重命名材质
original_material = line.split()[1]
new_material = current_materials.get(original_material, original_material)
merged_mtl.append(f"newmtl {new_material}\n")
elif line.startswith(("map_Kd", "map_Ka", "map_bump")):
# 替换贴图路径为相对路径
texture_name = os.path.basename(texture_file)
merged_mtl.append(f"{line.split()[0]} {texture_name}\n")
texture_files.add(texture_file)
else:
merged_mtl.append(line)
material_offset += 1
# 写入合并后的 .obj 和 .mtl 文件
with open(output_obj, "w") as obj_out:
obj_out.writelines(merged_obj)
with open(output_mtl, "w") as mtl_out:
mtl_out.writelines(merged_mtl)
print(f"texture_files====={texture_files}")
# 将纹理文件复制到输出目录
for texture_file in texture_files:
shutil.copy(texture_file, output_group_folder)
print(f"合并完成:{output_obj}{output_mtl}")
print(f"纹理文件已复制到:{output_group_folder}")
def move_compact_obj_to_file(input_folder,output_folder):
""""""
group_size = 50
obj_pid_list = os.listdir(input_folder)
group_obj_list = [obj_pid_list[i:i + group_size] for i in range(0, len(obj_pid_list), group_size)]
print(group_obj_list)
for group_obj in group_obj_list:
print(f"group_obj{group_obj}")
out_obj_file_name = group_obj[0]+"_"+group_obj[-1]
print(f"out_obj_file_name:::{out_obj_file_name}")
group_out_put_dir = os.path.join(output_folder,out_obj_file_name)
os.makedirs(group_out_put_dir,exist_ok=True)
for obj_name in group_obj:
original_obj_dir = os.path.join(input_folder,obj_name)
for file_name in os.listdir(original_obj_dir):
original_path = os.path.join(original_obj_dir,file_name)
dis_path = os.path.join(group_out_put_dir,file_name)
shutil.copy(original_path,dis_path)
print("分组完成。")
if __name__ == '__main__':
input_folder = "/data/datasets_20t/type_setting_test_data/print_compact_obj/"
output_folder = "/data/datasets_20t/type_setting_test_data/obj_merger_result/"
#merged_obj_for_group(input_folder,output_folder)
move_compact_obj_to_file(input_folder, output_folder)

3619
print_mplot3d_point_cloud_layout.py

File diff suppressed because it is too large Load Diff

127
print_setting_run.py

@ -1,127 +0,0 @@ @@ -1,127 +0,0 @@
# -*- coding: utf-8 -*-
import pandas as pd
from threading import Thread
from PyQt5.QtWidgets import QApplication, QMainWindow,QMessageBox
from print_setting_ui import Ui_MainWindow
from PyQt5.QtGui import QFont
from PySide2.QtCore import Signal,QObject
from PyQt5.QtCore import QThread, pyqtSignal, QProcess
import sys
import os
import warnings
import time
import time
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QLabel,
QVBoxLayout, QFileDialog, QMessageBox)
from threading import Thread
from print_factory_type_setting_obj_run import print_type_setting_obj
warnings.filterwarnings('ignore')
pd.set_option('display.width', None)
class MySignals(QObject):
text_print = Signal(str)
update_table = Signal(str)
class MyMainForm(QMainWindow, Ui_MainWindow):
def __init__(self,parent=None):
super(MyMainForm, self).__init__(parent)
self.setupUi(self)
self.folder_path = ""
self.cache_path = ""
self.pushButton.clicked.connect(self.on_select_folder)
self.pushButton_2.clicked.connect(self.on_run_clicked)
self.pushButton_3.clicked.connect(self.on_open_output_clicked)
self.global_ms = MySignals()
self.global_ms.text_print.connect(self.printToGui)
def printToGui(self,text):
n_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
self.textBrowser.append(n_time+" "+str(text))
def on_select_folder(self):
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder:
self.folder_path = folder
#self.folder_path_label.setText(f"📂 当前选择文件夹: {folder}")
#self.run_status_label.setText("")
self.cache_path = folder + "_arrange"
os.makedirs(self.cache_path, exist_ok=True)
def on_run_clicked(self):
def threadFunc1():
if not self.folder_path:
#self.run_status_label.setText("❗请先选择一个文件夹再执行!")
return
# if hasattr(self, 'worker') and self.worker.isRunning():
# self.run_status_label.setText("⚠ 正在执行中,请稍候...")
# return
#self.run_btn.setEnabled(False)
#self.run_status_label.setText("🚀 程序正在运行,请稍候...")
print_type_setting_obj(
base_original_obj_dir=self.folder_path,
cache_type_setting_dir=self.cache_path,
show_chart=False
)
#self.run_status_label.setText("✅ 排版完成!")
thread = Thread(target=threadFunc1)
thread.start()
def open_file_cross_platform(self, path):
if not os.path.exists(path):
print("路径不存在!")
return
if sys.platform.startswith('win'):
os.startfile(path)
elif sys.platform.startswith('darwin'):
QProcess.startDetached("open", [path])
else:
QProcess.startDetached("xdg-open", [path])
def on_open_output_clicked(self):
output_path = os.path.join(self.cache_path, "print_compact_obj")
if os.path.exists(output_path):
self.open_file_cross_platform(output_path)
else:
self.run_status_label.setText(" 输出文件夹不存在!")
def douyin_spider_go(self):
"""下载抖音视频"""
def threadFunc1():
print("开始下载")
self.load_chrome_video()
thread = Thread(target=threadFunc1)
thread.start()
#thread.run()
def open_config_dir(self):
"""打开文件夹"""
def threadFunc1():
try:
start_directory = os.path.join(self.dir_base)
os.startfile(start_directory)
except RecursionError:
print("打开配置文件夹失败。")
thread = Thread(target=threadFunc1)
thread.start()
if __name__ == '__main__':
#multiprocessing.freeze_support()
app = QApplication(sys.argv)
myWin = MyMainForm()
myWin.show()
sys.exit(app.exec_())

51
print_setting_ui.py

@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'dou_spider_ui.ui'
#
# Created by: PyQt5 UI code generator 5.15.4
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(300, 150)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
self.verticalLayout.setObjectName("verticalLayout")
#self.folder_path_label = QLabel("📁 请选择要排版的文件夹")
#self.folder_path_label.setWordWrap(True)
# 按钮布局
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.verticalLayout.addWidget(self.pushButton)
self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_2.setObjectName("pushButton_2")
self.verticalLayout.addWidget(self.pushButton_2)
self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
self.pushButton_3.setObjectName("pushButton_3")
self.verticalLayout.addWidget(self.pushButton_3)
MainWindow.setCentralWidget(self.centralwidget)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "自动排版工具"))
self.pushButton.setText(_translate("MainWindow", "选择文件夹"))
self.pushButton_2.setText(_translate("MainWindow", "开始自动排版"))
self.pushButton_3.setText(_translate("MainWindow", "打开排版好的文件夹"))

314
print_show_weight_max_obj.py

@ -17,10 +17,7 @@ from multiprocessing import Pool, RawArray @@ -17,10 +17,7 @@ from multiprocessing import Pool, RawArray
import ctypes
import itertools
from get_lowest_position_of_center_ext import get_lowest_position_of_center_ext
from get_lowest_position_of_center_ext import get_lowest_position_of_center_ext2
from get_lowest_position_of_center_ext import get_lowest_position_of_center_ext3
from get_lowest_position_of_center_ext import get_lowest_position_of_center_net
from compute_print_net import down_sample
def make_pcd_plane():
# 创建Y=0的平面点云
@ -360,22 +357,6 @@ def parallel_rotation4(points, angle_step=4): @@ -360,22 +357,6 @@ def parallel_rotation4(points, angle_step=4):
#"""
def read_mesh(obj_path, simple=True):
mesh_obj = o3d.io.read_triangle_mesh(obj_path, enable_post_processing=True) #
return mesh_obj
if not simple:
return mesh_obj
original_triangles = len(mesh_obj.triangles)
target_triangles = original_triangles if original_triangles <= 10000 else 10000
if original_triangles > 10000:
mesh_obj = mesh_obj.simplify_quadric_decimation(
target_number_of_triangles=target_triangles,
maximum_error=0.0001,
boundary_weight=1.0
)
return mesh_obj
def compute_mesh_center(vertices):
"""
计算网格质心
@ -403,28 +384,6 @@ def compute_mesh_center(vertices): @@ -403,28 +384,6 @@ def compute_mesh_center(vertices):
centroid = np.array([sum_x / n, sum_y / n, sum_z / n])
return centroid
def down_sample(pcd, voxel_size, farthest_sample = False):
original_num = len(pcd.points)
target_samples = 1500 # 1000
num_samples = min(target_samples, original_num)
# 第一步:使用体素下采样快速减少点数量
# voxel_size = 3
if farthest_sample:
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_voxel = pcd.voxel_down_sample(voxel_size)
down_num = len(pcd_voxel.points)
# print(f"original_num={original_num}, down_num={down_num}")
# 第二步:仅在必要时进行最远点下采样
if len(pcd_voxel.points) > target_samples and False:
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples)
else:
pcd_downsampled = pcd_voxel
return pcd_downsampled
def get_lowest_position_of_center(obj_path,voxel_size,dict_origin,total_matrix):
mesh_obj = read_mesh(obj_path)
@ -728,153 +687,6 @@ def axis_angle_to_rotation_matrix(axis, angle): @@ -728,153 +687,6 @@ def axis_angle_to_rotation_matrix(axis, angle):
cos_a + axis[2]**2*(1-cos_a)]
])
def arrange_box_correctly(obj_transformed, voxel_size,total_matrix):
vertices = np.asarray(obj_transformed.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
# 降采样与特征计算
pcd_downsampled = down_sample(pcd, voxel_size)
original_num = len(pcd.points)
target_samples = 1000
points = np.asarray(pcd_downsampled.points)
cov = np.cov(points.T)
center = obj_transformed.get_center()
# 特征分解与方向约束(关键修改点)
eigen_vals, eigen_vecs = np.linalg.eigh(cov)
max_axis = eigen_vecs[:, np.argmax(eigen_vals)]
# arrow = o3d.geometry.TriangleMesh.create_arrow(0.05, 0.1)
# arrow.translate(center)
# o3d.visualization.draw_geometries([obj_transformed, arrow])
# print("max_axis", max_axis)
# 强制主方向向量X分量为正(指向右侧)
if max_axis[0] < 0 or (max_axis[0] == 0 and max_axis[1] < 0):
max_axis = -max_axis
target_dir = np.array([1, 0]) # 目标方向为X正轴
current_dir = max_axis[:2] / np.linalg.norm(max_axis[:2])
dot_product = np.dot(current_dir, target_dir)
# print("dot_product", dot_product)
if dot_product < 0.8: # 阈值控制方向敏感性(建议0.6~0.9)
max_axis = -max_axis # 强制翻转方向
# 计算旋转角度
angle_z = np.arctan2(max_axis[1], max_axis[0]) % (2 * np.pi)
if max_axis[0] <= 0 and max_axis[1] <= 0:
angle_z += np.pi
# print("max_axis2", max_axis, angle_z / np.pi * 180 % 360)
# angle_z = 0
R = o3d.geometry.get_rotation_matrix_from_axis_angle([0, 0, -angle_z])
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = center - R.dot(center) # 保持中心不变
obj_transformed.transform(T)
total_matrix = T @ total_matrix
#arrow = o3d.geometry.TriangleMesh.create_arrow(0.05, 0.1)
#arrow.translate(center)
#o3d.visualization.draw_geometries([obj_transformed, arrow])
return obj_transformed, total_matrix
def get_new_bbox(obj_transformed_second,obj_name,weight_fix_out_dir,weight_fix_out_ply_dir,voxel_size,show_chart,dict_fix,compact_min_dis,total_matrix):
# 计算点云的边界
points = np.asarray(obj_transformed_second.vertices)
min_bound = np.min(points, axis=0) # 获取点云的最小边界
max_bound = np.max(points, axis=0) # 获取点云的最大边界
# 确保包围盒的Y坐标不低于0
min_bound[2] = max(min_bound[2], 0) # 确保Y坐标的最小值不低于0
# 重新计算包围盒的中心和半长轴
bbox_center = (min_bound + max_bound) / 2 # 计算包围盒的中心点
bbox_extent = (max_bound - min_bound) # 计算包围盒的半长轴(尺寸)
# 创建包围盒,确保尺寸正确
new_bbox = o3d.geometry.OrientedBoundingBox(center=bbox_center,
R=np.eye(3), # 旋转矩阵,默认没有旋转
extent=bbox_extent)
# 获取包围盒的长、宽和高
x_length = round(bbox_extent[0],3) # X 方向的长
y_length = round(bbox_extent[1],3) # Y 方向的宽
z_length = round(bbox_extent[2],3) # Z 方向的高
bbox_points = np.array([
[min_bound[0], min_bound[1], min_bound[2]],
[max_bound[0], min_bound[1], min_bound[2]],
[max_bound[0], max_bound[1], min_bound[2]],
[min_bound[0], max_bound[1], min_bound[2]],
[min_bound[0], min_bound[1], max_bound[2]],
[max_bound[0], min_bound[1], max_bound[2]],
[max_bound[0], max_bound[1], max_bound[2]],
[min_bound[0], max_bound[1], max_bound[2]]
])
first_corner = bbox_points[2]
translation_vector = -first_corner
# start_time = time.time()
obj_transformed_second.translate(translation_vector)
T_trans = np.eye(4)
T_trans[:3, 3] = translation_vector # 设置平移分量 [2,3](@ref)
total_matrix = T_trans @ total_matrix # 矩阵乘法顺序:最新变换左乘[4,5](@ref)
new_bbox.translate(translation_vector)
# print("get_new_bbox1",time.time()-start_time)
vertices = np.asarray(obj_transformed_second.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(vertices)
ply_print_pid = obj_name.replace(".obj","")
ply_name = f"{ply_print_pid}={z_length}+{y_length}+{x_length}.ply"
ply_out_path = os.path.join(weight_fix_out_ply_dir,ply_name)
# o3d.io.write_point_cloud(ply_out_path, pcd_downsampled)
# o3d.io.write_point_cloud(ply_out_path, pcd)
if compact_min_dis:
original_num = len(pcd.points)
target_samples = 1500 # 1000
num_samples = min(target_samples, original_num)
start_time = time.time()
pcd_downsampled = down_sample(pcd, voxel_size, False)
# print("down_sample time =",time.time()-start_time)
dict_fix[ply_name] = pcd_downsampled
else:
dict_fix[ply_name] = pcd
# print("dict_fix write",ply_name)
# print("voxel_down_sample&&write_point_cloud",time.time()-start_time)
if show_chart:
# 创建包围盒的轮廓(线框)
new_bbox_lines = o3d.geometry.LineSet.create_from_oriented_bounding_box(new_bbox)
new_bbox_lines.paint_uniform_color([1, 0, 0]) # 红色
#创建坐标系
coordinate_frame = o3d.geometry.TriangleMesh.create_coordinate_frame(size=2, origin=[0, 0, 0])
# 创建Y=0的平面点云
pcd_plane = make_pcd_plane()
# 可视化点云和边界框
o3d.visualization.draw_geometries([obj_transformed_second, coordinate_frame, pcd_plane, new_bbox_lines])
return obj_transformed_second, total_matrix
def load_obj_data(get_pid,base_out_dir):
"""下载obj文件"""
access_key_id = os.getenv('OSS_TEST_ACCESS_KEY_ID', 'LTAI5tBWbfkZntfJij4Fg9gz')
@ -910,130 +722,6 @@ def load_obj_data(get_pid,base_out_dir): @@ -910,130 +722,6 @@ def load_obj_data(get_pid,base_out_dir):
# 如果迭代器没有返回任何对象,那么表示该"文件夹"不存在
print("ossFolder does not exist.",get_pid)
def custom_mesh_transform(vertices, transform_matrix):
"""
手动实现网格变换对每个顶点应用齐次变换矩阵
参数:
vertices: 网格顶点数组 (N, 3)
transform_matrix: 4x4 齐次变换矩阵
返回:
变换后的顶点数组 (N, 3)
"""
# 1. 顶点转齐次坐标 (N, 3) → (N, 4)
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1))))
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN)
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T
# 3. 转回非齐次坐标 (3xN) → (N, 3)
transformed_vertices = transformed_homogeneous[:3, :].T
return transformed_vertices
def make_bbox_for_print(base_out_dir,weight_fix_out_dir,weight_fix_out_ply_dir,show_chart,dict_bad, dict_best_angel,dict_fix,dict_origin,dict_origin_real, compact_min_dis,dict_total_matrix):
"""获取需要的盒子大小"""
# 加载点云数据
start_time1 = time.time()
obj_id_list = [aa.split(".o")[0] for aa in os.listdir(base_out_dir) if aa.endswith(".obj")]
#print(obj_id_list)
#print(len(obj_id_list))
#random.shuffle(obj_id_list)
obj_id_list = obj_id_list
#print(obj_id_list)
voxel_size = 3 # 设置体素的大小,决定下采样的密度
#for pid in tqdm(obj_id_list,desc="get new bbox"):
dict_mesh_obj = {}
for pid_t_y in obj_id_list:
start_time2 = time.time()
obj_name = pid_t_y+".obj"
obj_path = os.path.join(base_out_dir,obj_name)
total_matrix = np.eye(4)
#放置最大接触面
# obj_transformed, total_matrix = get_lowest_position_of_center(obj_path,voxel_size,dict_origin,total_matrix)
mesh_obj = read_mesh(obj_path)
# dict_origin_real[obj_path] = copy.deepcopy(mesh_obj)
dict_origin[obj_path] = copy.deepcopy(mesh_obj)
start_time3 = time.time()
total_matrix, z_min= get_lowest_position_of_center_ext(obj_path, total_matrix)
# print("get_lowest_position_of_center_ext time", time.time()-start_time3)
# print(f"total_matrix={total_matrix}")
print(f"z_min={z_min}")
printId = ""
match = re.search(r"P(\d+)", obj_name) # 匹配 "P" 后的连续数字
if match:
printId = match.group(1)
# print("printId", printId)
# total_matrix, z_mean_min = get_lowest_position_of_center_net(printId, total_matrix)
# print("total_matrix=", total_matrix)
original_vertices = np.asarray(mesh_obj.vertices)
transformed_vertices = custom_mesh_transform(original_vertices, total_matrix)
mesh_obj.vertices = o3d.utility.Vector3dVector(transformed_vertices)
# print("dict_origin[] obj_path=", obj_path)
# dict_origin[obj_path] = mesh_obj
obj_transformed = copy.deepcopy(mesh_obj)
translation = total_matrix[:3, 3]
# print("make_bbox_for_print0", obj_name, translation)
if obj_transformed is None:
dict_bad[obj_name]=obj_name
# print(len(dict_bad))
# print(obj_name)
# 记录错误文件
error_log = os.path.join(base_out_dir, "error_files.txt")
with open(error_log, 'a') as f:
f.write(f"{obj_path}\n")
print(f"Skipping invalid file: {obj_path}")
continue
start_time3 = time.time()
best_angle_x, best_angle_y, best_angle_z, z_mean_min = get_lowest_position_of_center_ext3(mesh_obj, obj_path,voxel_size)
# print("get_lowest_position_of_center_ext2 time", time.time()-start_time3)
# print("best_angle=", best_angle_x, best_angle_y, best_angle_z, z_mean_min)
dict_best_angel[obj_name] = [int(round(best_angle_x)), int(round(best_angle_y)), int(round(best_angle_z))]
start_time3 = time.time()
#将点云摆正和X轴平衡
obj_transformed_second,total_matrix = arrange_box_correctly(obj_transformed,voxel_size,total_matrix)
# print("arrange_box_correctly time", time.time()-start_time3)
"""
# 创建可视化窗口
vis = o3d.visualization.Visualizer()
vis.create_window(window_name='模型展示')
# 添加所有模型到场景
vis.add_geometry(obj_transformed_second)
# 设置相机视角
vis.get_render_option().mesh_show_back_face = True
vis.get_render_option().light_on = True
# 运行可视化
vis.run()
vis.destroy_window()
#"""
#print("摆正后的obj")
#o3d.visualization.draw_geometries([obj_transformed_second, ])
start_time3 = time.time()
mesh_obj,total_matrix = get_new_bbox(obj_transformed_second,obj_name,weight_fix_out_dir,weight_fix_out_ply_dir,voxel_size,show_chart,dict_fix,compact_min_dis,total_matrix)
dict_mesh_obj[obj_name] = mesh_obj
# print("get_new_bbox time", time.time()-start_time3)
dict_total_matrix[obj_name] = total_matrix
print(f"make_bbox_for_print {obj_name} time={time.time()-start_time2}")
print(f"make_bbox_for_print total_time={time.time()-start_time1}")
return dict_mesh_obj
import re
def copy_obj_2x(base_obj_dir):
obj_list = [aa for aa in os.listdir(base_obj_dir) if aa.endswith(".obj")]

346
print_type_setting_gui.py

@ -1,346 +0,0 @@ @@ -1,346 +0,0 @@
import sys
import os
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QLabel,
QVBoxLayout, QFileDialog, QMessageBox,
QRadioButton, QHBoxLayout, QButtonGroup, QGroupBox)
from PyQt5.QtCore import QProcess, QTimer
from threading import Thread
import print_factory_type_setting_obj_run
class AutoLayoutApp(QWidget):
def __init__(self):
super().__init__()
self.small_model_temp = False
self.setWindowTitle("自动排版工具")
self.setGeometry(200, 200, 600, 380) # 增加窗口高度以容纳新控件
self.dw = print_factory_type_setting_obj_run
self.folder_path = ""
self.cache_path = ""
self.process = None
if self.small_model_temp:
self.selected_mode = "紧凑" # 默认排版模式
self.output_format = "模型" # 默认输出格式
self.selected_machine = "小机型" # 默认机型
else:
self.selected_mode = "标准" # 默认排版模式
self.output_format = "JSON" # 默认输出格式
self.selected_machine = "大机型" # 默认机型
self.is_running = False # 跟踪排版状态
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
# ===== 机型选择区域(移到最上面) =====
machine_group = QGroupBox("机型选择")
machine_layout = QHBoxLayout()
# 创建机型选择按钮组
self.machine_group = QButtonGroup(self)
# 创建两种机型选项
self.large_machine = QRadioButton("大机型(600 * 500 * 300)")
self.small_machine = QRadioButton("小机型(380 * 345 * 250)")
if self.small_model_temp:
self.large_machine.setEnabled(False)
self.small_machine.setChecked(True) # 默认选中
else:
self.large_machine.setChecked(True) # 默认选中
# 添加到按钮组
self.machine_group.addButton(self.large_machine, 1)
self.machine_group.addButton(self.small_machine, 2)
# 连接信号
self.machine_group.buttonToggled.connect(self.on_machine_changed)
# 添加到布局
machine_layout.addWidget(self.large_machine)
machine_layout.addWidget(self.small_machine)
machine_group.setLayout(machine_layout)
layout.addWidget(machine_group)
# ===== 排版模式选择区域(改为QGroupBox) =====
mode_group = QGroupBox("排版模式")
mode_layout = QHBoxLayout()
# 创建单选按钮组
self.mode_group = QButtonGroup(self)
# 创建三种排版模式选项
self.standard_mode = QRadioButton("标准模式(适合规整模型组)")
self.compact_mode = QRadioButton("紧凑模式(复杂度较高))")
self.advanced_mode = QRadioButton("高级模式(复杂度最高)")
self.advanced_mode.setVisible(False)
if self.small_model_temp:
self.standard_mode.setEnabled(False)
self.compact_mode.setChecked(True) # 默认选中
else:
self.standard_mode.setChecked(True) # 默认选中
# 添加到按钮组(确保互斥选择)
self.mode_group.addButton(self.standard_mode, 1)
self.mode_group.addButton(self.compact_mode, 2)
self.mode_group.addButton(self.advanced_mode, 3)
# 连接信号
self.mode_group.buttonToggled.connect(self.on_mode_changed)
# 添加到布局
mode_layout.addWidget(self.standard_mode)
mode_layout.addWidget(self.compact_mode)
mode_layout.addWidget(self.advanced_mode)
mode_group.setLayout(mode_layout)
layout.addWidget(mode_group)
# ==== 输出格式选择区域 ====
format_group = QGroupBox("输出格式")
format_layout = QHBoxLayout()
# 创建输出格式按钮组
self.format_group = QButtonGroup(self)
# 创建两种输出格式选项
self.json_format = QRadioButton("JSON格式")
self.model_format = QRadioButton("模型格式")
if self.small_model_temp:
self.json_format.setEnabled(False)
self.model_format.setChecked(True) # 默认选中
else:
self.json_format.setChecked(True) # 默认选中
# 添加到按钮组
self.format_group.addButton(self.json_format, 1)
self.format_group.addButton(self.model_format, 2)
# 连接信号
self.format_group.buttonToggled.connect(self.on_format_changed)
# 添加到布局
format_layout.addWidget(self.json_format)
format_layout.addWidget(self.model_format)
format_group.setLayout(format_layout)
layout.addWidget(format_group)
# ===== 原有UI元素 =====
self.folder_path_label = QLabel(" 请选择要排版的文件夹")
self.folder_path_label.setWordWrap(True)
self.run_status_label = QLabel("")
layout.addWidget(self.folder_path_label)
layout.addWidget(self.run_status_label)
self.select_folder_btn = QPushButton("选择文件夹")
self.select_folder_btn.clicked.connect(self.on_select_folder)
layout.addWidget(self.select_folder_btn)
# 按钮布局(运行按钮和预览按钮在同一行)
buttons_layout = QHBoxLayout()
self.run_btn = QPushButton("开始自动排版")
self.run_btn.clicked.connect(self.on_run_clicked)
buttons_layout.addWidget(self.run_btn)
# ===== 新增预览按钮 =====
self.preview_btn = QPushButton("预览排版结果")
self.preview_btn.clicked.connect(self.on_preview_clicked)
self.preview_btn.setEnabled(False) # 初始禁用,排版完成后才可用
buttons_layout.addWidget(self.preview_btn)
layout.addLayout(buttons_layout)
self.open_output_btn = QPushButton("打开排版好的文件夹")
self.open_output_btn.clicked.connect(self.on_open_output_clicked)
layout.addWidget(self.open_output_btn)
self.setLayout(layout)
def on_mode_changed(self, button, checked):
"""处理排版模式选择变化"""
if checked:
# self.selected_mode = button.text().replace("模式", "")
self.selected_mode = button.text()[:2]
self.run_status_label.setText(f"已选择: {self.selected_mode} 模式")
def on_format_changed(self, button, checked):
"""处理输出格式选择变化"""
if checked:
self.output_format = button.text().replace("格式", "")
self.run_status_label.setText(f"输出格式: {self.output_format}")
def on_machine_changed(self, button, checked):
"""处理机型选择变化"""
if checked:
self.selected_machine = button.text().split("(")[0]
self.run_status_label.setText(f"已选择: {self.selected_machine}")
def on_select_folder(self):
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder:
self.folder_path = folder
self.folder_path_label.setText(f" 当前选择文件夹: {folder}")
self.run_status_label.setText("")
self.cache_path = folder + "_arrange"
# self.cache_path = os.path.join(folder, "arrange")
os.makedirs(self.cache_path, exist_ok=True)
self.preview_btn.setEnabled(False) # 选择新文件夹后禁用预览按钮
def get_base_directory(self):
"""获取脚本或可执行文件的基础目录"""
if getattr(sys, 'frozen', False):
# 打包后的可执行文件环境
base_path = os.path.dirname(sys.executable)
else:
# 正常脚本运行环境
base_path = os.path.dirname(os.path.abspath(__file__))
return base_path
def on_run_clicked(self):
# 获取脚本所在目录的父目录
script_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(script_dir)
# 获取基础目录
base_path = self.get_base_directory()
# 获取父目录
parent_dir = os.path.dirname(base_path)
bad_dir = os.path.join(parent_dir, "bad")
full_dir = os.path.join(parent_dir, "full")
# 检查bad目录
bad_dir_exists = os.path.exists(bad_dir) and os.path.isdir(bad_dir)
bad_dir_not_empty = bad_dir_exists and any(os.scandir(bad_dir))
# 检查full目录
full_dir_exists = os.path.exists(full_dir) and os.path.isdir(full_dir)
full_dir_not_empty = full_dir_exists and any(os.scandir(full_dir))
# 如果有异常数据需要处理
if bad_dir_not_empty or full_dir_not_empty:
message = "请处理以下目录中的异常数据:\n"
if bad_dir_not_empty:
message += f"- bad目录: {bad_dir}\n"
if full_dir_not_empty:
message += f"- full目录: {full_dir}\n"
QMessageBox.warning(self, "存在未处理的异常数据",
message + "\n请先处理这些目录中的数据后再进行排版!")
self.run_status_label.setText(" 存在未处理的异常数据,请先处理!")
return
def threadFunc1():
self.is_running = True # 标记排版开始
if not self.folder_path:
self.run_status_label.setText("❗请先选择一个文件夹再执行!")
self.is_running = False
return
if self.process and self.process.state() == QProcess.Running:
self.run_status_label.setText(" 正在执行中,请稍候...")
self.is_running = False
return
self.run_btn.setEnabled(False)
self.preview_btn.setEnabled(False) # 排版中禁用预览按钮
self.run_status_label.setText(
f" 正在使用 [{self.selected_mode}] 排版, 机型: {self.selected_machine}, 输出格式: {self.output_format}, 请稍候..."
)
normalized_path = os.path.normpath(self.folder_path)
self.batch_id = os.path.basename(normalized_path)
# 调用排版函数,传递所有参数
self.dw.print_type_setting_obj(
base_original_obj_dir=self.folder_path,
cache_type_setting_dir=self.cache_path,
show_chart=False,
batch_id=self.batch_id,
selected_mode=self.selected_mode,
output_format=self.output_format,
selected_machine=self.selected_machine
)
self.run_status_label.setText(
f"✅ [{self.selected_mode}] 排版完成! 机型: {self.selected_machine}, 输出格式: {self.output_format}"
)
self.run_btn.setEnabled(True)
self.preview_btn.setEnabled(True) # 排版完成后启用预览按钮
self.is_running = False # 标记排版结束
thread = Thread(target=threadFunc1)
thread.start()
def on_process_finished(self, exitCode, exitStatus):
self.run_btn.setEnabled(True)
self.preview_btn.setEnabled(True) # 完成后启用预览按钮
if exitCode == 0:
self.run_status_label.setText("✅ 排版完成!")
else:
self.run_status_label.setText(f"❌❌ 进程异常退出 (代码: {exitCode})")
def on_process_error(self, error):
self.run_btn.setEnabled(True)
self.preview_btn.setEnabled(False) # 出错时禁用预览按钮
self.run_status_label.setText(f"❌❌ 发生错误: {error.name}")
QMessageBox.critical(self, "错误", f"进程执行出错: {error.name}")
def open_file_cross_platform(self, path):
if not os.path.exists(path):
self.run_status_label.setText(" 路径不存在!")
return
if sys.platform.startswith('win'):
os.startfile(path)
elif sys.platform.startswith('darwin'):
QProcess.startDetached("open", [path])
else:
QProcess.startDetached("xdg-open", [path])
def on_preview_clicked(self):
"""预览按钮点击事件处理"""
if not self.cache_path:
self.run_status_label.setText(" 请先执行排版操作!")
return
is_small_machine = self.selected_machine=="小机型"
if os.path.exists(self.folder_path):
self.run_status_label.setText("正在打开预览目录...")
self.dw.preview(
base_original_obj_dir=self.folder_path,batch_id=self.batch_id, is_small_machine=is_small_machine
)
else:
self.run_status_label.setText(" 预览目录不存在!")
def on_open_output_clicked(self):
"""打开排版结果文件夹"""
if not self.cache_path:
self.run_status_label.setText(" 请先执行排版操作!")
return
open_dir = self.cache_path
if self.output_format=="JSON":
open_dir = self.folder_path
if os.path.exists(open_dir):
self.open_file_cross_platform(open_dir)
else:
self.run_status_label.setText(" 输出文件夹不存在!")
if __name__ == "__main__":
from PyQt5.QtCore import QSharedMemory
app = QApplication(sys.argv)
shared_mem = QSharedMemory("AutoLayoutTool_unique_key")
if not shared_mem.create(1):
QMessageBox.critical(None, "错误", "程序已经在运行中!")
sys.exit(1)
window = AutoLayoutApp()
window.show()
sys.exit(app.exec_())

38
print_type_setting_gui.spec

@ -1,38 +0,0 @@ @@ -1,38 +0,0 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['print_type_setting_gui.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='print_type_setting_gui',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

126
print_type_setting_gui_multi.py

@ -1,126 +0,0 @@ @@ -1,126 +0,0 @@
import sys
import os
from PyQt5.QtWidgets import (QApplication, QWidget, QPushButton, QLabel,
QVBoxLayout, QFileDialog, QMessageBox)
from PyQt5.QtCore import QProcess, QTimer
from threading import Thread
import print_factory_type_setting_obj_run
class AutoLayoutApp(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("自动排版工具")
self.setGeometry(200, 200, 600, 200)
self.dw= print_factory_type_setting_obj_run
self.folder_path = ""
self.cache_path = ""
self.process = None
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
self.folder_path_label = QLabel("<EFBFBD><EFBFBD> 请选择要排版的文件夹")
self.folder_path_label.setWordWrap(True)
self.run_status_label = QLabel("")
layout.addWidget(self.folder_path_label)
layout.addWidget(self.run_status_label)
self.select_folder_btn = QPushButton("选择文件夹")
self.select_folder_btn.clicked.connect(self.on_select_folder)
layout.addWidget(self.select_folder_btn)
self.run_btn = QPushButton("开始自动排版")
self.run_btn.clicked.connect(self.on_run_clicked)
layout.addWidget(self.run_btn)
self.open_output_btn = QPushButton("打开排版好的文件夹")
self.open_output_btn.clicked.connect(self.on_open_output_clicked)
layout.addWidget(self.open_output_btn)
self.setLayout(layout)
def on_select_folder(self):
folder = QFileDialog.getExistingDirectory(self, "选择文件夹")
if folder:
self.folder_path = folder
self.folder_path_label.setText(f"<EFBFBD><EFBFBD> 当前选择文件夹: {folder}")
self.run_status_label.setText("")
self.cache_path = folder + "_arrange"
os.makedirs(self.cache_path, exist_ok=True)
def on_run_clicked(self):
def threadFunc1():
if not self.folder_path:
self.run_status_label.setText("❗请先选择一个文件夹再执行!")
return
if self.process and self.process.state() == QProcess.Running:
self.run_status_label.setText(" 正在执行中,请稍候...")
return
self.run_btn.setEnabled(False)
self.run_status_label.setText("<EFBFBD><EFBFBD> 程序正在运行,请稍候...")
return_code = self.dw.print_type_setting_obj(base_original_obj_dir=self.folder_path,cache_type_setting_dir=self.cache_path,
show_chart=False)
if return_code==0:
self.run_status_label.setText("✅ 排版完成!")
elif return_code==-1:
self.run_status_label.setText("❌选择目录为空!")
else:
self.run_status_label.setText("❌排版失败!")
self.run_btn.setEnabled(True)
thread = Thread(target=threadFunc1)
thread.start()
def on_process_finished(self, exitCode, exitStatus):
self.run_btn.setEnabled(True)
if exitCode == 0:
self.run_status_label.setText("✅ 排版完成!")
else:
self.run_status_label.setText(f"❌ 进程异常退出 (代码: {exitCode})")
def on_process_error(self, error):
self.run_btn.setEnabled(True)
self.run_status_label.setText(f"❌ 发生错误: {error.name}")
QMessageBox.critical(self, "错误", f"进程执行出错: {error.name}")
def open_file_cross_platform(self, path):
if not os.path.exists(path):
self.run_status_label.setText(" 路径不存在!")
return
if sys.platform.startswith('win'):
os.startfile(path)
elif sys.platform.startswith('darwin'):
QProcess.startDetached("open", [path])
else:
QProcess.startDetached("xdg-open", [path])
def on_open_output_clicked(self):
if not self.cache_path:
self.run_status_label.setText(" 请先执行排版操作!")
return
output_path = os.path.join(self.cache_path, "print_compact_obj")
if os.path.exists(output_path):
self.open_file_cross_platform(output_path)
else:
self.run_status_label.setText(" 输出文件夹不存在!")
if __name__ == "__main__":
from PyQt5.QtCore import QSharedMemory
app = QApplication(sys.argv)
shared_mem = QSharedMemory("AutoLayoutTool_unique_key")
if not shared_mem.create(1):
QMessageBox.critical(None, "错误", "程序已经在运行中!")
sys.exit(1)
window = AutoLayoutApp()
window.show()
sys.exit(app.exec_())

25
qt5_demo.py

@ -1,25 +0,0 @@ @@ -1,25 +0,0 @@
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox
class MyWindow(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("PyQt5 简单示例")
self.setGeometry(100, 100, 300, 200)
self.button = QPushButton("点我一下", self)
self.button.setGeometry(100, 80, 100, 30)
self.button.clicked.connect(self.show_message)
def show_message(self):
QMessageBox.information(self, "提示", "按钮被点击了!")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
"""
pyinstaller qt5_demo.py --hidden-import PySide2.QtXml
"""

44
qt5_demo.spec

@ -1,44 +0,0 @@ @@ -1,44 +0,0 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['qt5_demo.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=['PySide2.QtXml'],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='qt5_demo',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='qt5_demo',
)

275
sui_01.py

@ -1,275 +0,0 @@ @@ -1,275 +0,0 @@
import open3d as o3d
import os
import numpy as np
from scipy.spatial.transform import Rotation
import sys
import argparse
# import cv2
import matplotlib.pyplot as plt
import numpy as np
from numba import njit, prange
import time
# 核心计算函数(支持Numba加速)
@njit(fastmath=True, cache=True)
def calculate_rotation_z(angle_x, angle_y, angle_z, points, cos_cache, sin_cache, angle_step):
"""计算单个旋转组合后的重心Z坐标(无显式平移)"""
# 获取预计算的三角函数值
idx_x = angle_x // angle_step
idx_y = angle_y // angle_step
idx_z = angle_z // angle_step
cos_x = cos_cache[idx_x]
sin_x = sin_cache[idx_x]
cos_y = cos_cache[idx_y]
sin_y = sin_cache[idx_y]
cos_z = cos_cache[idx_z]
sin_z = sin_cache[idx_z]
# 构造旋转矩阵(展开矩阵乘法优化)
# R = Rz @ Ry @ Rx
# 计算矩阵元素(手动展开矩阵乘法)
m00 = cos_z * cos_y
m01 = cos_z * sin_y * sin_x - sin_z * cos_x
m02 = cos_z * sin_y * cos_x + sin_z * sin_x
m10 = sin_z * cos_y
m11 = sin_z * sin_y * sin_x + cos_z * cos_x
m12 = sin_z * sin_y * cos_x - cos_z * sin_x
m20 = -sin_y
m21 = cos_y * sin_x
m22 = cos_y * cos_x
# 计算所有点的Z坐标
z_values = np.empty(points.shape[0], dtype=np.float64)
for i in prange(points.shape[0]):
x, y, z = points[i, 0], points[i, 1], points[i, 2]
# 应用旋转矩阵
rotated_z = m20 * x + m21 * y + m22 * z
z_values[i] = rotated_z
# 计算重心Z(等效于平移后的重心)
min_z = np.min(z_values)
avg_z = np.mean(z_values)
return avg_z - min_z # 等效于平移后的重心Z坐标
# 并行优化主函数
def parallel_rotation2(points, angle_step=3):
"""
参数
points : numpy.ndarray (N,3) - 三维点云
angle_step : int - 角度搜索步长度数
返回
(best_angle_x, best_angle_y, best_angle_z, min_z)
"""
points = np.ascontiguousarray(points.astype(np.float64))
# 生成所有可能角度
angles = np.arange(0, 360, angle_step)
n_angles = len(angles)
# 预计算三角函数值(大幅减少重复计算)
rads = np.radians(angles)
cos_cache = np.cos(rads).astype(np.float64)
sin_cache = np.sin(rads).astype(np.float64)
# 生成所有角度组合(内存优化版)
total_combinations = n_angles ** 3
print(f"Total combinations: {total_combinations:,}")
# 分块处理以避免内存溢出
best_z = np.inf
best_angles = (0, 0, 0)
batch_size = 10 ** 6 # 根据可用内存调整
for x_chunk in range(0, n_angles, max(1, n_angles // 4)):
angles_x = angles[x_chunk:x_chunk + max(1, n_angles // 4)]
for y_chunk in range(0, n_angles, max(1, n_angles // 4)):
angles_y = angles[y_chunk:y_chunk + max(1, n_angles // 4)]
# 生成当前分块的所有组合
xx, yy, zz = np.meshgrid(angles_x, angles_y, angles)
current_batch = np.stack([xx.ravel(), yy.ravel(), zz.ravel()], axis=1)
# 处理子批次
for i in range(0, len(current_batch), batch_size):
batch = current_batch[i:i + batch_size]
results = np.zeros(len(batch), dtype=np.float64)
_process_batch(batch, points, cos_cache, sin_cache, angle_step, results)
# 更新最佳结果
min_idx = np.argmin(results)
if results[min_idx] < best_z:
best_z = results[min_idx]
best_angles = tuple(batch[min_idx])
print(f"New best: {best_angles} -> Z={best_z:.4f}")
return (*best_angles, best_z)
@njit(parallel=True, fastmath=True)
def _process_batch(batch, points, cos_cache, sin_cache, angle_step, results):
for i in prange(len(batch)):
ax, ay, az = batch[i]
results[i] = calculate_rotation_z(
ax, ay, az, points,
cos_cache, sin_cache, angle_step
)
class ModelProcessor:
def __init__(self):
# argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
parser = argparse.ArgumentParser()
parser.add_argument(
"--id",
required=False,
)
args = parser.parse_args()
self.id = args.id
self.mesh = None
self.asset_dir = f"/home/algo/Documents/datasets/{self.id}"
def load_model(self):
"""加载并初始化3D模型"""
# model_path = f"{self.asset_dir}/baked/{self.id}.obj"
# model_path = f"{self.asset_dir}/repair_{self.id}_mesh.ply"
model_path = "/data/datasets_20t/8/88884_253283_P65951_6cm_x1.obj"
if not os.path.exists(model_path):
raise FileNotFoundError(f"Model file not found: {model_path}")
print(model_path)
mesh_native = o3d.io.read_triangle_mesh(model_path, enable_post_processing=False)
# self.mesh = o3d.io.read_triangle_mesh(model_path, enable_post_processing=False)
print("Open3D去重前顶点数:", len(mesh_native.vertices))
self.mesh = mesh_native.merge_close_vertices(eps=1e-6)
vertices2 = np.asarray(self.mesh.vertices)
print("Open3D去重后顶点数:", len(vertices2))
vertices2_sorted = sorted(
vertices2.tolist(),
key=lambda x: (x[0], x[1], x[2])
)
if not self.mesh.has_vertex_colors():
num_vertices = len(self.mesh.vertices)
self.mesh.vertex_colors = o3d.utility.Vector3dVector(
np.ones((num_vertices, 3))
)
self.uv_array = np.asarray(self.mesh.triangle_uvs)
# print(f"UV 坐标形状:{self.uv_array.shape}, {self.uv_array[0][1]}")
def calculate_rotation_and_center_of_mass(self, angle_x, angle_y, angle_z, points):
"""计算某一组旋转角度后的重心"""
# 计算绕X轴、Y轴和Z轴的旋转矩阵
R_x = np.array([
[1, 0, 0],
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
])
R_y = np.array([
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
[0, 1, 0],
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
])
R_z = np.array([
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
[0, 0, 1]
])
# 综合旋转矩阵
R = R_z @ R_y @ R_x
# 执行旋转
rotated_points = points @ R.T
# 计算最小z值
min_z = np.min(rotated_points[:, 2])
# 计算平移向量,将最小Z值平移到0
translation_vector = np.array([0, 0, -min_z])
rotated_points += translation_vector
# 计算重心
center_of_mass = np.mean(rotated_points, axis=0)
return center_of_mass[2], angle_x, angle_y, angle_z
def parallel_rotation(self, angle_step=3):
"""顺序计算最优旋转角度(单线程)"""
min_center_of_mass_y = float('inf')
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
# 遍历所有角度组合
for angle_x in range(0, 360, angle_step):
for angle_y in range(0, 360, angle_step):
for angle_z in range(0, 360, angle_step):
center_of_mass_z, ax, ay, az = self.calculate_rotation_and_center_of_mass(
angle_x, angle_y, angle_z, self.mesh.vertices
)
if center_of_mass_z < min_center_of_mass_y:
min_center_of_mass_y = center_of_mass_z
best_angle_x, best_angle_y, best_angle_z = ax, ay, az
return best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y
def process(self):
"""执行完整处理流程"""
self.load_model()
try:
start = time.time()
# mesh = o3d.geometry.TriangleMesh()
# mesh.vertices = o3d.utility.Vector3dVector(np.random.rand(100, 3))
# points = np.asarray(mesh.vertices)
pcd = o3d.geometry.PointCloud()
pcd.points = o3d.utility.Vector3dVector(self.mesh.vertices)
# 自动计算合理体素大小
raw_points = np.asarray(pcd.points)
bounds = np.ptp(raw_points, axis=0)
voxel_size = np.max(bounds) / 50 # 默认取最大边长的2%
# 执行下采样并验证
pcd_downsampled = pcd.voxel_down_sample(voxel_size)
if len(pcd_downsampled.points) < 10: # 最少保留10个点
raise RuntimeError(f"下采样失败:voxel_size={voxel_size:.3f}过大")
print(f"下采样后点数: {len(pcd_downsampled.points)} (voxel_size={voxel_size:.3f})")
# pcd.paint_uniform_color([1,0,0]) # 原始红色
# pcd_downsampled.paint_uniform_color([0,0,1]) # 采样后蓝色
# o3d.visualization.draw_geometries([pcd, pcd_downsampled])
# 继续后续处理
points = np.asarray(pcd_downsampled.points)
best_angle_x, best_angle_y, best_angle_z, min_z = parallel_rotation2(points, angle_step=5)
print("best=", best_angle_x, best_angle_y, best_angle_z, min_z)
print(time.time() - start)
except Exception as e:
print(f"Error during processing: {str(e)}")
raise
if __name__ == "__main__":
ModelProcessor().process()

19
test.py

@ -1,19 +0,0 @@ @@ -1,19 +0,0 @@
import requests
import json
printId = 84258
url = f"https://mp.api.suwa3d.com/api/printOrder/infoByPrintId?printId={printId}"
res = requests.get(url)
print(res)
datas = res.json()["data"]["layout"]
print(datas)
angle_x = datas.get("angle_x",0)
angle_y = datas.get("angle_y",0)
angle_z = datas.get("angle_z",0)
layout_z = datas.get("layout_z",0)
print("angle_x",angle_x)
print("angle_y",angle_y)
print("angle_z",angle_z)
print("layout_z",layout_z)
# TODO 解析 res

34
test_load_json.py

@ -5,24 +5,8 @@ import os @@ -5,24 +5,8 @@ import os
from PIL import Image
import argparse
def custom_mesh_transform(vertices, transform_matrix):
"""
手动实现网格变换对每个顶点应用齐次变换矩阵
参数:
vertices: 网格顶点数组 (N, 3)
transform_matrix: 4x4 齐次变换矩阵
返回:
变换后的顶点数组 (N, 3)
"""
# 1. 顶点转齐次坐标 (N, 3) → (N, 4)
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1))))
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN)
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T
# 3. 转回非齐次坐标 (3xN) → (N, 3)
transformed_vertices = transformed_homogeneous[:3, :].T
return transformed_vertices
from config import print_factory_type_dir
from general import mesh_transform_by_matrix
def load_and_transform_models(base_path, dict_origin, blank_path, json_name):
@ -46,6 +30,8 @@ def load_and_transform_models(base_path, dict_origin, blank_path, json_name): @@ -46,6 +30,8 @@ def load_and_transform_models(base_path, dict_origin, blank_path, json_name):
print(f"读取JSON文件失败: {e}")
return []
print(f"选择机型={data.get('summary')['selected_machine']}")
# 处理每个模型
for model in data.get('models', []):
obj_name = model.get('file_name', '')
@ -86,7 +72,7 @@ def load_and_transform_models(base_path, dict_origin, blank_path, json_name): @@ -86,7 +72,7 @@ def load_and_transform_models(base_path, dict_origin, blank_path, json_name):
# 手动变换顶点
original_vertices = np.asarray(mesh.vertices)
transformed_vertices = custom_mesh_transform(original_vertices, reconstructed_matrix)
transformed_vertices = mesh_transform_by_matrix(original_vertices, reconstructed_matrix)
mesh.vertices = o3d.utility.Vector3dVector(transformed_vertices)
# 添加到列表
@ -467,16 +453,16 @@ def load_show_save(base_path, dict_origin, blank_path, batch_id, is_show=False): @@ -467,16 +453,16 @@ def load_show_save(base_path, dict_origin, blank_path, batch_id, is_show=False):
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("--batch_id", type=str, required=True, help="batch_id")
parser.add_argument("--batch_id", type=str, required=False, help="batch_id")
args = parser.parse_args()
# batch_id = args.batch_id
batch_id = "9" # 1113-MY-4
batch_id = "test" # 1113-MY-4
print_factory_type_dir="/root/print_factory_type"
base_path = f"{print_factory_type_dir}/{batch_id}/"
# blank_path = "{print_factory_type_dir}/blank/blank_bias/blank2.obj"
blank_path = f"{print_factory_type_dir}/blank/blank_bias/blank_small.obj"
blank_path = f"{print_factory_type_dir}/blank/blank_bias/blank2.obj"
# blank_path = f"{print_factory_type_dir}/blank/blank_bias/blank_small.obj"
print(f"blank_path={blank_path}")
load_show_save(base_path, {}, blank_path, batch_id, True)

52
x_y_min_test.py

@ -1,52 +0,0 @@ @@ -1,52 +0,0 @@
import os
import shutil
import time
import random
import matplotlib.pyplot as plt
import open3d as o3d
import numpy as np
# ply_read_path="/data/datasets_20t/type_setting_test_data/print_bounds_compact_data/88884_253283_P65951_6cm_x1=7.811+11.043+25.699.ply"
# # 读取点云
# pcd = o3d.io.read_point_cloud(ply_read_path)
#
# # 获取点云的点数据
# points = np.asarray(pcd.points)
#
# # 计算质心
# centroid = np.mean(points, axis=0)
#
# # 计算 Y 轴最小值
# min_y_value = np.min(points[:, 1]) # Y 轴最小值
# max_y_value = np.max(points[:, 1])
#
# # 计算 X 轴最小值
# min_x_value = np.min(points[:, 0]) # X 轴最小值
#
# print(f'min_x_value{min_x_value}')
# min_x_value -385.08287729332403
#
ply_read_path="/data/datasets_20t/type_setting_test_data/print_bounds_compact_data/456450_260316_P65976_2.66cm_x1=21.778+22.904+26.333.ply"
# 读取点云
pcd = o3d.io.read_point_cloud(ply_read_path)
# 获取点云的点数据
points = np.asarray(pcd.points)
# 计算质心
centroid = np.mean(points, axis=0)
# 计算 Y 轴最小值
min_y_value = np.min(points[:, 1]) # Y 轴最小值
max_y_value = np.max(points[:, 1])
# 计算 X 轴最小值
min_x_value = np.min(points[:, 0]) # X 轴最小值
print(f'min_x_value{min_x_value}')
# min_x_value -385.08287729332403
print(f'min_y_value{min_y_value}')
# -339

152
读写时间测试.py

@ -1,152 +0,0 @@ @@ -1,152 +0,0 @@
# import numpy as np
# def calculate_rotation_and_center_of_mass(angle_x, angle_y, angle_z, points):
# """计算某一组旋转角度后的重心"""
# # 计算绕X轴、Y轴和Z轴的旋转矩阵
# R_x = np.array([
# [1, 0, 0],
# [0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
# [0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
# ])
#
# R_y = np.array([
# [np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
# [0, 1, 0],
# [-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
# ])
#
# R_z = np.array([
# [np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
# [np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
# [0, 0, 1]
# ])
#
# # 综合旋转矩阵
# R = R_z @ R_y @ R_x
#
# # 执行旋转
# rotated_points = points @ R.T
#
# # 计算最小z值
# min_z = np.min(rotated_points[:, 2])
#
# # 计算平移向量,将最小Z值平移到0
# translation_vector = np.array([0, 0, -min_z])
# rotated_points += translation_vector
#
# # 计算重心
# center_of_mass = np.mean(rotated_points, axis=0)
#
# return center_of_mass[2], angle_x, angle_y, angle_z
#
# def parallel_rotation(points, angle_step=3):
# """顺序计算最优旋转角度(单线程)"""
# min_center_of_mass_y = float('inf')
# best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
#
# # 遍历所有角度组合
# for angle_x in range(0, 360, angle_step):
# for angle_y in range(0, 360, angle_step):
# for angle_z in range(0, 360, angle_step):
# center_of_mass_z, ax, ay, az = calculate_rotation_and_center_of_mass(
# angle_x, angle_y, angle_z, points
# )
# if center_of_mass_z < min_center_of_mass_y:
# min_center_of_mass_y = center_of_mass_z
# best_angle_x, best_angle_y, best_angle_z = ax, ay, az
#
# return best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y
import bpy
import time
import os
from pathlib import Path
def clear_scene():
"""清空当前场景中的所有对象"""
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
def test_bpy_io(obj_path, output_path):
"""测试 bpy 的 OBJ 读写性能"""
# 读取 OBJ
start_time = time.time()
bpy.ops.import_scene.obj(filepath=obj_path)
read_time = time.time() - start_time
# 确保场景中有对象
if not bpy.context.scene.objects:
raise ValueError("未成功导入 OBJ 文件")
# 写入 OBJ
start_time = time.time()
bpy.ops.export_scene.obj(
filepath=output_path,
use_selection=False, # 导出所有对象
use_materials=False, # 不导出材质(加快速度)
)
write_time = time.time() - start_time
# 清理场景
clear_scene()
return write_time, read_time
def test_folder_objs_with_bpy(folder_path, output_folder="output_objs_bpy"):
"""测试文件夹中所有 OBJ 文件的读写性能(使用 bpy)"""
# 确保输出文件夹存在
Path(output_folder).mkdir(exist_ok=True)
# 收集所有 OBJ 文件
obj_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.obj')]
if not obj_files:
print(f"在文件夹 {folder_path} 中未找到 OBJ 文件")
return
print(f"找到 {len(obj_files)} 个 OBJ 文件,开始测试...")
results = []
for obj_file in obj_files:
input_path = os.path.join(folder_path, obj_file)
output_path = os.path.join(output_folder, f"bpy_{obj_file}")
print(f"\n测试文件: {obj_file}")
try:
write_time, read_time = test_bpy_io(input_path, output_path)
file_size = os.path.getsize(input_path) / (1024 * 1024) # MB
print(f" 文件大小: {file_size:.2f} MB")
print(f" bpy 读取时间: {read_time:.3f}s")
print(f" bpy 写入时间: {write_time:.3f}s")
results.append({
"filename": obj_file,
"size_mb": file_size,
"read_time": read_time,
"write_time": write_time,
})
except Exception as e:
print(f" 处理 {obj_file} 时出错: {e}")
# 计算平均时间
if results:
avg_read = sum(r["read_time"] for r in results) / len(results)
avg_write = sum(r["write_time"] for r in results) / len(results)
print("\n=== 汇总结果 ===")
print(f"平均读取时间: {avg_read:.3f}s")
print(f"平均写入时间: {avg_write:.3f}s")
if __name__ == "__main__":
# 设置包含OBJ文件的文件夹路径
obj_folder = "/data/datasets_20t/9_big/" # 替换为你的OBJ文件夹路径
# 运行测试
test_folder_objs_with_bpy(obj_folder)

98
读写时间测试2.py

@ -1,98 +0,0 @@ @@ -1,98 +0,0 @@
import os
import time
import open3d as o3d
import trimesh
from pathlib import Path
def test_folder_objs(folder_path, output_folder="output_objs"):
"""测试文件夹中所有OBJ文件的读写性能"""
# 确保输出文件夹存在
Path(output_folder).mkdir(exist_ok=True)
# 收集文件夹中所有OBJ文件
obj_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.obj')]
if not obj_files:
print(f"在文件夹 {folder_path} 中未找到OBJ文件")
return
print(f"找到 {len(obj_files)} 个OBJ文件,开始测试...")
# 准备结果记录
results = []
for obj_file in obj_files:
file_path = os.path.join(folder_path, obj_file)
output_path = os.path.join(output_folder, obj_file)
print(f"\n测试文件: {obj_file}")
# 测试open3d
o3d_write, o3d_read = test_open3d_io(file_path, output_path.replace('.obj', '_o3d.obj'))
# 测试trimesh
tm_write, tm_read = test_trimesh_io(file_path, output_path.replace('.obj', '_tm.obj'))
# 记录结果
file_stats = {
'filename': obj_file,
'o3d_write': o3d_write,
'o3d_read': o3d_read,
'tm_write': tm_write,
'tm_read': tm_read,
'write_ratio': o3d_write / tm_write if tm_write > 0 else 0,
'read_ratio': o3d_read / tm_read if tm_read > 0 else 0
}
results.append(file_stats)
# 打印当前文件结果
print(f" open3d | 写入: {o3d_write:.3f}s | 读取: {o3d_read:.3f}s")
print(f" trimesh | 写入: {tm_write:.3f}s | 读取: {tm_read:.3f}s")
print(f" 写入速度比(trimesh/open3d): {file_stats['write_ratio']:.1f}x")
print(f" 读取速度比(trimesh/open3d): {file_stats['read_ratio']:.1f}x")
# 打印汇总结果
print("\n=== 汇总结果 ===")
avg_write_ratio = sum(r['write_ratio'] for r in results) / len(results)
avg_read_ratio = sum(r['read_ratio'] for r in results) / len(results)
print(f"平均写入速度比(trimesh/open3d): {avg_write_ratio:.1f}x")
print(f"平均读取速度比(trimesh/open3d): {avg_read_ratio:.1f}x")
def test_open3d_io(input_path, output_path):
"""测试open3d的读写性能"""
# 读取
start = time.time()
mesh = o3d.io.read_triangle_mesh(input_path)
read_time = time.time() - start
# 写入
start = time.time()
o3d.io.write_triangle_mesh(output_path, mesh)
write_time = time.time() - start
return write_time, read_time
def test_trimesh_io(input_path, output_path):
"""测试trimesh的读写性能"""
# 读取
start = time.time()
mesh = trimesh.load(input_path)
read_time = time.time() - start
# 写入
start = time.time()
mesh.export(output_path)
write_time = time.time() - start
return write_time, read_time
if __name__ == "__main__":
# 设置包含OBJ文件的文件夹路径
obj_folder = "/data/datasets_20t/9_big/" # 替换为你的OBJ文件夹路径
# 运行测试
test_folder_objs(obj_folder)
Loading…
Cancel
Save