42 changed files with 3032 additions and 7317 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -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] |
||||||
@ -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 |
||||||
|
|
||||||
|
# -------------------------- 结束:碰撞检测和越界 -------------------------- |
||||||
@ -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) |
|
||||||
@ -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) |
|
||||||
|
|
||||||
@ -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']}, |
|
||||||
""" |
|
||||||
|
|
||||||
@ -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) |
|
||||||
@ -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) |
|
||||||
File diff suppressed because it is too large
Load Diff
@ -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", "打开排版好的文件夹")) |
|
||||||
|
|
||||||
@ -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, |
|
||||||
) |
|
||||||
@ -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 |
|
||||||
""" |
|
||||||
@ -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', |
|
||||||
) |
|
||||||
@ -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() |
|
||||||
@ -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 |
|
||||||
@ -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 |
|
||||||
@ -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) |
|
||||||
@ -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…
Reference in new issue