You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

298 lines
10 KiB

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, enable_post_processing=False):
mesh_obj = o3d.io.read_triangle_mesh(obj_path, enable_post_processing)
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
del homogeneous_vertices
del transformed_homogeneous
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 = read_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
def get_blank_path(is_small_machine=False):
if is_small_machine:
return os.path.join(print_factory_type_dir, "print_setting/blank/blank_small.obj")
else:
return os.path.join(print_factory_type_dir, "print_setting/blank/blank_big.obj")
# -------------------------- 结束:模型 ----------------------------------
# -------------------------- 开始:碰撞检测和越界 --------------------------
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
extend_dist_min_collision = 3
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
# -------------------------- 结束:碰撞检测和越界 --------------------------