代码更新
This commit is contained in:
1 parent
1b0ecb2727
commit
dcc9a27cc2
7 files changed
+646
-627
No files matched your search
+40
-485
@@ -3,352 +3,14 @@ import numpy as np
|
||||
import copy
|
||||
import time
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from general import *
|
||||
|
||||
# ------------------------ 开始:对外接口,获取bbox数据 --------------------
|
||||
|
||||
"""
|
||||
对外部提供的获取bbox数据的接口
|
||||
compute_bbox_out
|
||||
|
||||
参数:
|
||||
obj_path, 模型数据路径
|
||||
|
||||
返回:
|
||||
total_matrix: 旋转矩阵, 16位浮点型, 例如, [[ 0.13644984 0.99064698 0. -49.71074343]
|
||||
[ -0.99064698 0.13644984 0. -28.80249299]
|
||||
[ 0. 0. 1. 3.26326203]
|
||||
[ 0. 0. 0. 1. ]]
|
||||
z_max : z最高点, 1位浮点型, 例如, 1.0
|
||||
min_bound : bbox最低点, 3位浮点型, 例如, [-1.0, -1.0, 0.0]
|
||||
max_bound : bbox最低点, 3位浮点型, 例如, [0.0, 0.0, 1.0]
|
||||
ply_name : ply的名字, 字符串, 例如, 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply
|
||||
"""
|
||||
|
||||
def compute_bbox_out(mesh_obj):
|
||||
return compute_bbox_ext(mesh_obj)
|
||||
|
||||
# -------------------------- 结束:对外接口,获取bbox数据 ----------------
|
||||
|
||||
# -------------------------- 开始:获取z值最低 --------------------------
|
||||
|
||||
def get_lowest_position_of_z_ext(mesh_obj):
|
||||
|
||||
total_matrix = np.eye(4)
|
||||
|
||||
voxel_size = 3
|
||||
|
||||
# print(f"obj_path={obj_path}, get_lowest_position_of_center voxel_size={voxel_size}")
|
||||
start_time1 = time.time()
|
||||
|
||||
vertices = np.asarray(mesh_obj.vertices)
|
||||
|
||||
# 确保网格有顶点
|
||||
if len(vertices) == 0:
|
||||
# raise ValueError(f"Mesh has no vertices: {obj_path}")
|
||||
print(f"Warning: Mesh has no vertices: {mesh_obj}")
|
||||
return None
|
||||
|
||||
pcd = o3d.geometry.PointCloud()
|
||||
pcd.points = o3d.utility.Vector3dVector(vertices)
|
||||
|
||||
# print("voxel_size",voxel_size,obj_path, len(pcd.points), len(mesh_obj.vertices))
|
||||
|
||||
# 对点云进行下采样(体素网格法)
|
||||
#"""
|
||||
pcd_downsampled = down_sample(pcd, voxel_size)
|
||||
pcd_downsampled.paint_uniform_color([0, 0, 1])
|
||||
|
||||
if len(np.asarray(pcd_downsampled.points)) <= 0:
|
||||
bbox = pcd.get_axis_aligned_bounding_box()
|
||||
volume = bbox.volume()
|
||||
|
||||
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}")
|
||||
|
||||
# 处理体积为零的情况
|
||||
if volume <= 0:
|
||||
# 计算点云的实际范围
|
||||
points = np.asarray(pcd.points)
|
||||
if len(points) > 0:
|
||||
min_bound = np.min(points, axis=0)
|
||||
max_bound = np.max(points, axis=0)
|
||||
extent = max_bound - min_bound
|
||||
|
||||
# 确保最小维度至少为0.01
|
||||
min_dimension = max(0.01, np.min(extent))
|
||||
volume = min_dimension ** 3
|
||||
else:
|
||||
volume = 1.0 # 最后的安全回退
|
||||
|
||||
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}")
|
||||
|
||||
# 安全计算密度 - 防止除零错误
|
||||
if len(pcd.points) > 0 and volume > 0:
|
||||
original_density = len(pcd.points) / volume
|
||||
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33)))
|
||||
else:
|
||||
# 当点数为0或体积为0时使用默认体素大小
|
||||
voxel_size = 1.0 # 默认值
|
||||
|
||||
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}")
|
||||
|
||||
pcd_downsampled = down_sample(pcd, voxel_size)
|
||||
pcd_downsampled.paint_uniform_color([0, 0, 1])
|
||||
|
||||
original_num = len(pcd.points)
|
||||
target_samples = 1000
|
||||
num_samples = min(target_samples, original_num)
|
||||
|
||||
# print("get_lowest_position_of_center1 time", time.time()-start_time1)
|
||||
start_time2 = time.time()
|
||||
# 确保下采样后有点云
|
||||
if len(np.asarray(pcd_downsampled.points)) == 0:
|
||||
# 使用原始点云作为后备
|
||||
pcd_downsampled = pcd
|
||||
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points")
|
||||
|
||||
points = np.asarray(pcd_downsampled.points)
|
||||
|
||||
# 初始化最小重心Y的值
|
||||
max_z_of_mass_y = float('inf')
|
||||
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
|
||||
best_angle_x, best_angle_y, best_angle_z, max_z_of_mass_y = parallel_rotation(points, angle_step=3)
|
||||
|
||||
# 使用最佳角度进行旋转并平移obj
|
||||
pcd_transformed = copy.deepcopy(mesh_obj)
|
||||
|
||||
# 最佳角度旋转
|
||||
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x))
|
||||
pcd_transformed.rotate(R_x)
|
||||
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y))
|
||||
pcd_transformed.rotate(R_y)
|
||||
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z))
|
||||
pcd_transformed.rotate(R_z)
|
||||
|
||||
T_x = np.eye(4)
|
||||
T_x[:3, :3] = R_x
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_x @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
T_y = np.eye(4)
|
||||
T_y[:3, :3] = R_y
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_y @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
T_z = np.eye(4)
|
||||
T_z[:3, :3] = R_z
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_z @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
#试着旋转180,让脸朝上
|
||||
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
# 计算平移向量,将最小Y值平移到0
|
||||
min_z = np.min(vertices[:, 2])
|
||||
translation_vector = np.array([0,0,-min_z,])
|
||||
pcd_transformed.translate(translation_vector)
|
||||
|
||||
T_trans1 = np.eye(4)
|
||||
T_trans1[:3, 3] = translation_vector
|
||||
total_matrix = T_trans1 @ total_matrix
|
||||
|
||||
# 计算 z 坐标均值
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
z_mean1 = np.mean(vertices[:, 2])
|
||||
z_max1 = np.max(vertices[:, 2])
|
||||
|
||||
volume_centroid = get_volume_centroid(pcd_transformed)
|
||||
z_volume_center1 = volume_centroid[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)
|
||||
pcd_transformed.translate(-center_point)
|
||||
pcd_transformed.rotate(R_y, center=(0, 0, 0))
|
||||
pcd_transformed.translate(center_point)
|
||||
|
||||
aabb = pcd_transformed.get_axis_aligned_bounding_box()
|
||||
# center_point = aabb.get_center()
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
# 构建绕中心点旋转的变换矩阵[3](@ref)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
R_y180 = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
|
||||
T_rotate = np.eye(4)
|
||||
T_rotate[:3, :3] = R_y180
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_rotate @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
#print("旋转后质心:", pcd_transformed.get_center())
|
||||
#print("旋转后点示例:", np.asarray(pcd_transformed.vertices)[:3])
|
||||
|
||||
#
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
# 计算平移向量,将最小Y值平移到0
|
||||
min_z = np.min(vertices[:, 2])
|
||||
max_z = np.max(vertices[:, 2])
|
||||
# print("min_z1", min_z, obj_path)
|
||||
translation_vector = np.array([0,0,-min_z,])
|
||||
# translation_vector = np.array([0,0,-min_z + (min_z-max_z),])
|
||||
# print("translation_vector1",translation_vector)
|
||||
pcd_transformed.translate(translation_vector)
|
||||
|
||||
T_trans2 = np.eye(4)
|
||||
T_trans2[:3, 3] = translation_vector
|
||||
translation = total_matrix[:3, 3]
|
||||
# print("translation_vector2",translation_vector)
|
||||
# print(1,translation)
|
||||
|
||||
total_matrix = T_trans2 @ total_matrix
|
||||
translation = total_matrix[:3, 3]
|
||||
# print(2,translation)
|
||||
|
||||
# 计算 z 坐标均值
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
z_mean2 = np.mean(vertices[:, 2])
|
||||
z_max2 = np.max(vertices[:, 2])
|
||||
volume_centroid = get_volume_centroid(pcd_transformed)
|
||||
z_volume_center2 = volume_centroid[2]
|
||||
|
||||
# print(f"get_lowest_position_of_center z_max1={z_max1}, z_max2={z_max2}, len={len(pcd_transformed.vertices)}, obj_path={obj_path}")
|
||||
|
||||
# print(f"z_mean1={z_mean1}, z_mean2={z_mean2}")
|
||||
# if (z_mean2 > z_mean1):
|
||||
# print(f"z_volume_center1={z_volume_center1}, z_volume_center2={z_volume_center2}")
|
||||
if (z_volume_center2 > z_volume_center1):
|
||||
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
|
||||
centroid = pcd_transformed.get_center()
|
||||
|
||||
aabb = pcd_transformed.get_axis_aligned_bounding_box()
|
||||
# center_point = aabb.get_center()
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
|
||||
pcd_transformed.translate(-center_point)
|
||||
pcd_transformed.rotate(R_y, center=(0, 0, 0))
|
||||
pcd_transformed.translate(center_point)
|
||||
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
# 构建反向旋转矩阵
|
||||
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
|
||||
T_rotate_inv = np.eye(4)
|
||||
T_rotate_inv[:3, :3] = R_y
|
||||
# 完整的反向绕中心旋转矩阵
|
||||
T_rot_center_inv = T_origin_to_center @ T_rotate_inv @ T_center_to_origin
|
||||
total_matrix = T_rot_center_inv @ total_matrix
|
||||
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
# 计算平移向量,将最小Y值平移到0
|
||||
min_z = np.min(vertices[:, 2])
|
||||
# print("min_z2", min_z, obj_path)
|
||||
translation_vector = np.array([0,0,-min_z,])
|
||||
pcd_transformed.translate(translation_vector)
|
||||
|
||||
T_trans3 = np.eye(4)
|
||||
T_trans3[:3, 3] = translation_vector
|
||||
total_matrix = T_trans3 @ total_matrix
|
||||
|
||||
# z_mean_min = min(z_mean1, z_mean2)
|
||||
z_max = min(z_max1, z_max2)
|
||||
|
||||
# print("get_lowest_position_of_center2 time", time.time()-start_time2)
|
||||
return total_matrix, z_max
|
||||
|
||||
def get_lowest_position_of_center_ext(mesh_obj, total_matrix):
|
||||
print(f"get_lowest_position_of_center_ext mesh_obj={mesh_obj}")
|
||||
temp_matrix, z_max = get_lowest_position_of_z_ext(mesh_obj)
|
||||
|
||||
total_matrix = temp_matrix @ total_matrix
|
||||
|
||||
return total_matrix, z_max
|
||||
|
||||
def calculate_rotation_and_top_of_mass(angle_x, angle_y, angle_z, points):
|
||||
"""计算某一组旋转角度后的重心"""
|
||||
# 计算绕X轴、Y轴和Z轴的旋转矩阵
|
||||
R_x = np.array([
|
||||
[1, 0, 0],
|
||||
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
|
||||
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
|
||||
])
|
||||
|
||||
R_y = np.array([
|
||||
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
|
||||
[0, 1, 0],
|
||||
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
|
||||
])
|
||||
|
||||
R_z = np.array([
|
||||
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
|
||||
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
|
||||
[0, 0, 1]
|
||||
])
|
||||
|
||||
# 综合旋转矩阵
|
||||
R = R_z @ R_y @ R_x
|
||||
|
||||
# 执行旋转
|
||||
rotated_points = points @ R.T
|
||||
|
||||
# 计算最小z值
|
||||
min_z = np.min(rotated_points[:, 2])
|
||||
|
||||
# 计算平移向量,将最小Z值平移到0
|
||||
translation_vector = np.array([0, 0, -min_z])
|
||||
rotated_points += translation_vector
|
||||
|
||||
top_of_mass = np.max(rotated_points, axis=0)
|
||||
|
||||
return top_of_mass[2], angle_x, angle_y, angle_z
|
||||
|
||||
def parallel_rotation(points, angle_step=4):
|
||||
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)"""
|
||||
max_top = float('inf')
|
||||
|
||||
for angle_x in range(-90, 90, angle_step):
|
||||
for angle_y in range(0, 360, angle_step):
|
||||
max_z, ax, ay, _ = calculate_rotation_and_top_of_mass(angle_x, angle_y, 0, points)
|
||||
if max_z < max_top:
|
||||
max_top = max_z
|
||||
best_angle_x = ax
|
||||
best_angle_y = ay
|
||||
|
||||
return (best_angle_x, best_angle_y, 0, max_top)
|
||||
|
||||
def compute_mesh_center(vertices):
|
||||
|
||||
if len(vertices) == 0:
|
||||
raise ValueError("顶点数组不能为空")
|
||||
|
||||
# 确保vertices是NumPy数组
|
||||
vertices_np = np.asarray(vertices)
|
||||
|
||||
# 使用NumPy的mean函数直接计算均值(向量化操作)
|
||||
centroid = np.mean(vertices_np, axis=0)
|
||||
|
||||
return centroid
|
||||
|
||||
# -------------------------- 结束:获取z值最低 --------------------------
|
||||
from compute_print_net_out import get_volume_centroid
|
||||
from compute_print_net_out import get_lowest_position_of_z_ext
|
||||
from compute_print_net_out import get_lowest_position_of_z_ext
|
||||
from compute_print_net_out import compute_bbox_ext
|
||||
from compute_print_net_out import voxel_size
|
||||
|
||||
# -------------------------- 开始:bbox --------------------------
|
||||
|
||||
@@ -439,12 +101,23 @@ def compute_bbox_all(dict_mesh_obj,is_downsample):
|
||||
|
||||
return dict_total_matrix,all_models
|
||||
|
||||
|
||||
def compute_bbox(mesh_obj, obj_name="", is_downsample=True):
|
||||
def compute_bbox(mesh_obj, obj_name, is_downsample=True):
|
||||
# return compute_bbox_ext(mesh_obj, obj_name, is_downsample)
|
||||
|
||||
mesh_obj_origin = copy.deepcopy(mesh_obj)
|
||||
total_matrix, z_max, min_bound, max_bound, ply_name = compute_bbox_ext(mesh_obj, obj_name, is_downsample)
|
||||
# print(f"compute_bbox_ext total_matrix = {total_matrix}, ply_name = {ply_name}")
|
||||
|
||||
"""
|
||||
# 从网络上获取数据
|
||||
printId = ""
|
||||
match = re.search(r"P(\d+)", obj_name) # 匹配 "P" 后的连续数字
|
||||
if match:
|
||||
printId = match.group(1)
|
||||
total_matrix, z_max, min_bound, max_bound, ply_name = compute_bbox_net(printId)
|
||||
ply_name = f"{os.path.splitext(obj_name)[0]}={ply_name.split('=')[1]}"
|
||||
# print(f"compute_bbox_net printId = {printId}, total_matrix = {total_matrix}, ply_name = {ply_name}")
|
||||
"""
|
||||
|
||||
transformed_vertices = mesh_transform_by_matrix(np.asarray(mesh_obj_origin.vertices), total_matrix)
|
||||
|
||||
@@ -458,141 +131,33 @@ def compute_bbox(mesh_obj, obj_name="", is_downsample=True):
|
||||
|
||||
return total_matrix, pcd_fix, ply_name
|
||||
|
||||
def compute_bbox_ext(mesh_obj, obj_name="", is_downsample=True):
|
||||
import requests
|
||||
import ast
|
||||
def compute_bbox_net(printId):
|
||||
|
||||
total_matrix = np.eye(4)
|
||||
total_matrix, z_max= get_lowest_position_of_center_ext(mesh_obj, total_matrix)
|
||||
url = f"https://mp.api.suwa3d.com/api/printOrder/infoByPrintId?printId={printId}"
|
||||
res = requests.get(url)
|
||||
|
||||
transformed_vertices = mesh_transform_by_matrix(np.asarray(mesh_obj.vertices), total_matrix)
|
||||
|
||||
obj_transformed = copy.deepcopy(mesh_obj)
|
||||
obj_transformed.vertices = o3d.utility.Vector3dVector(transformed_vertices)
|
||||
|
||||
voxel_size = 3 # 设置体素的大小,决定下采样的密度
|
||||
# 将点云摆正和X轴平衡
|
||||
obj_transformed_second,total_matrix = arrange_box_correctly(obj_transformed,voxel_size,total_matrix)
|
||||
|
||||
total_matrix, min_bound, max_bound, ply_name, pcd_fix = get_new_bbox(obj_transformed_second,obj_name,voxel_size,is_downsample,total_matrix)
|
||||
|
||||
del obj_transformed
|
||||
del obj_transformed_second
|
||||
datas = res.json()["data"]["layout"]
|
||||
# print("datas=", datas)
|
||||
|
||||
# return total_matrix, z_max, min_bound, max_bound, ply_name, pcd_fix
|
||||
return total_matrix, z_max, min_bound, max_bound, ply_name
|
||||
|
||||
def arrange_box_correctly(obj_transformed, voxel_size,total_matrix):
|
||||
|
||||
vertices = np.asarray(obj_transformed.vertices)
|
||||
pcd = o3d.geometry.PointCloud()
|
||||
pcd.points = o3d.utility.Vector3dVector(vertices)
|
||||
homo_matrix_str = datas.get("homo_matrix")
|
||||
# print("homo_matrix_str=", homo_matrix_str)
|
||||
|
||||
# 降采样与特征计算
|
||||
# pcd_downsampled = down_sample(pcd, voxel_size)
|
||||
# points = np.asarray(pcd_downsampled.points)
|
||||
points = np.asarray(pcd.points)
|
||||
|
||||
cov = np.cov(points.T)
|
||||
try:
|
||||
matrix_list = ast.literal_eval(homo_matrix_str) # 直接转换为二维列表
|
||||
reconstructed_matrix = np.array(matrix_list, dtype=float)
|
||||
print("矩阵形状:", reconstructed_matrix.shape) # 应该是 (4, 4)
|
||||
except (ValueError, SyntaxError) as e:
|
||||
print(f"解析错误: {e}")
|
||||
|
||||
center = obj_transformed.get_center()
|
||||
layout_z = datas.get("layout_z", 0)
|
||||
max_bound = datas.get("max_bound",0)
|
||||
min_bound = datas.get("min_bound",0)
|
||||
ply_name = datas.get("ply_name",0)
|
||||
# print(f"layout_z={layout_z}, max_bound={max_bound}, min_bound={min_bound}, ply_name={ply_name}, ")
|
||||
|
||||
eigen_vals, eigen_vecs = np.linalg.eigh(cov)
|
||||
max_axis = eigen_vecs[:, np.argmax(eigen_vals)]
|
||||
|
||||
# print("max_axis", max_axis)
|
||||
# 强制主方向向量X分量为正(指向右侧)
|
||||
if max_axis[0] < 0 or (max_axis[0] == 0 and max_axis[1] < 0):
|
||||
max_axis = -max_axis
|
||||
|
||||
target_dir = np.array([1, 0]) # 目标方向为X正轴
|
||||
current_dir = max_axis[:2] / np.linalg.norm(max_axis[:2])
|
||||
dot_product = np.dot(current_dir, target_dir)
|
||||
|
||||
# print("dot_product", dot_product)
|
||||
if dot_product < 0.8: # 阈值控制方向敏感性(建议0.6~0.9)
|
||||
max_axis = -max_axis # 强制翻转方向
|
||||
|
||||
# 计算旋转角度
|
||||
angle_z = np.arctan2(max_axis[1], max_axis[0]) % (2 * np.pi)
|
||||
|
||||
if max_axis[0] <= 0 and max_axis[1] <= 0:
|
||||
angle_z += np.pi
|
||||
|
||||
R = o3d.geometry.get_rotation_matrix_from_axis_angle([0, 0, -angle_z])
|
||||
|
||||
T = np.eye(4)
|
||||
T[:3, :3] = R
|
||||
T[:3, 3] = center - R.dot(center) # 保持中心不变
|
||||
obj_transformed.transform(T)
|
||||
|
||||
total_matrix = T @ total_matrix
|
||||
|
||||
return obj_transformed, total_matrix
|
||||
|
||||
def get_new_bbox(obj_transformed_second,obj_name,voxel_size,is_downsample,total_matrix):
|
||||
|
||||
# 计算点云的边界
|
||||
points = np.asarray(obj_transformed_second.vertices)
|
||||
|
||||
min_bound = np.min(points, axis=0) # 获取点云的最小边界
|
||||
max_bound = np.max(points, axis=0) # 获取点云的最大边界
|
||||
|
||||
# print(f"get_new_bbox1: min_bound={min_bound}, max_bound={max_bound}")
|
||||
|
||||
# 确保包围盒的Y坐标不低于0
|
||||
min_bound[2] = max(min_bound[2], 0) # 确保Y坐标的最小值不低于0
|
||||
|
||||
# 重新计算包围盒的中心和半长轴
|
||||
bbox_center = (min_bound + max_bound) / 2 # 计算包围盒的中心点
|
||||
bbox_extent = (max_bound - min_bound) # 计算包围盒的半长轴(尺寸)
|
||||
|
||||
# 创建包围盒,确保尺寸正确
|
||||
new_bbox = o3d.geometry.OrientedBoundingBox(center=bbox_center,
|
||||
R=np.eye(3), # 旋转矩阵,默认没有旋转
|
||||
extent=bbox_extent)
|
||||
# 获取包围盒的长、宽和高
|
||||
x_length = round(bbox_extent[0],3) # X 方向的长
|
||||
y_length = round(bbox_extent[1],3) # Y 方向的宽
|
||||
z_length = round(bbox_extent[2],3) # Z 方向的高
|
||||
bbox_points = np.array([
|
||||
[min_bound[0], min_bound[1], min_bound[2]],
|
||||
[max_bound[0], min_bound[1], min_bound[2]],
|
||||
[max_bound[0], max_bound[1], min_bound[2]],
|
||||
[min_bound[0], max_bound[1], min_bound[2]],
|
||||
[min_bound[0], min_bound[1], max_bound[2]],
|
||||
[max_bound[0], min_bound[1], max_bound[2]],
|
||||
[max_bound[0], max_bound[1], max_bound[2]],
|
||||
[min_bound[0], max_bound[1], max_bound[2]]
|
||||
])
|
||||
first_corner = bbox_points[2]
|
||||
translation_vector = -first_corner
|
||||
obj_transformed_second.translate(translation_vector)
|
||||
|
||||
T_trans = np.eye(4)
|
||||
T_trans[:3, 3] = translation_vector # 设置平移分量 [2,3](@ref)
|
||||
total_matrix = T_trans @ total_matrix # 矩阵乘法顺序:最新变换左乘[4,5](@ref)
|
||||
|
||||
new_bbox.translate(translation_vector)
|
||||
|
||||
vertices = np.asarray(obj_transformed_second.vertices)
|
||||
pcd = o3d.geometry.PointCloud()
|
||||
pcd.points = o3d.utility.Vector3dVector(vertices)
|
||||
if is_downsample:
|
||||
pcd_downsampled = down_sample(pcd, voxel_size, False)
|
||||
pcd_fix = pcd_downsampled
|
||||
else:
|
||||
pcd_fix = pcd
|
||||
|
||||
min_bound = np.min(pcd.points, axis=0) # 获取点云的最小边界
|
||||
max_bound = np.max(pcd.points, axis=0) # 获取点云的最大边界
|
||||
# print(f"get_new_bbox2: min_bound={min_bound}, max_bound={max_bound}")
|
||||
|
||||
if (not obj_name == ""):
|
||||
ply_print_pid = obj_name.replace(".obj","")
|
||||
ply_name = f"{ply_print_pid}={z_length}+{y_length}+{x_length}.ply"
|
||||
else:
|
||||
ply_name = ""
|
||||
|
||||
return total_matrix, min_bound, max_bound, ply_name, pcd_fix
|
||||
return reconstructed_matrix, layout_z, max_bound, min_bound, ply_name
|
||||
|
||||
class Platform:
|
||||
def __init__(self, width, depth, height):
|
||||
@@ -782,13 +347,3 @@ class Platform:
|
||||
return self.placed_models, self.unplaced_models
|
||||
|
||||
# -------------------------- 结束:bbox --------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--obj_path", type=str, required=True, help="batchobj_path_id")
|
||||
args = parser.parse_args()
|
||||
|
||||
obj_path = args.obj_path
|
||||
max, z = get_lowest_position_of_z_ext(obj_path)
|
||||
|
||||
@@ -0,0 +1,594 @@
|
||||
import open3d as o3d
|
||||
import numpy as np
|
||||
import copy
|
||||
import time
|
||||
import argparse
|
||||
import os
|
||||
|
||||
# ------------------------ 开始:对外接口,获取bbox数据 --------------------
|
||||
|
||||
"""
|
||||
对外部提供的获取bbox数据的接口
|
||||
compute_bbox_out
|
||||
|
||||
参数:
|
||||
obj_path: 模型数据路径
|
||||
|
||||
返回:
|
||||
total_matrix: 旋转矩阵, 16位浮点型, 例如, [[ 0.13644984 0.99064698 0. -49.71074343]
|
||||
[ -0.99064698 0.13644984 0. -28.80249299]
|
||||
[ 0. 0. 1. 3.26326203]
|
||||
[ 0. 0. 0. 1. ]]
|
||||
z_max : z最高点, 1位浮点型, 例如, 1.0
|
||||
min_bound : bbox最低点, 3位浮点型, 例如, [-1.0, -1.0, 0.0]
|
||||
max_bound : bbox最低点, 3位浮点型, 例如, [0.0, 0.0, 1.0]
|
||||
ply_name : ply的名字, 字符串, 例如, 857420_268473_P85240_5cm_x1=9.41+49.997+49.997.ply
|
||||
"""
|
||||
|
||||
def compute_bbox_out(obj_path):
|
||||
obj_name = os.path.basename(obj_path)
|
||||
|
||||
mesh_obj = read_mesh(obj_path)
|
||||
|
||||
return compute_bbox_ext(mesh_obj, obj_name)
|
||||
|
||||
# -------------------------- 结束:对外接口,获取bbox数据 ----------------
|
||||
|
||||
# -------------------------- 开始:获取z值最低 --------------------------
|
||||
|
||||
def get_lowest_position_of_z_ext(mesh_obj):
|
||||
|
||||
total_matrix = np.eye(4)
|
||||
|
||||
voxel_size = 3
|
||||
|
||||
# print(f"obj_path={obj_path}, get_lowest_position_of_center voxel_size={voxel_size}")
|
||||
start_time1 = time.time()
|
||||
|
||||
vertices = np.asarray(mesh_obj.vertices)
|
||||
|
||||
# 确保网格有顶点
|
||||
if len(vertices) == 0:
|
||||
# raise ValueError(f"Mesh has no vertices: {obj_path}")
|
||||
print(f"Warning: Mesh has no vertices: {mesh_obj}")
|
||||
return None
|
||||
|
||||
pcd = o3d.geometry.PointCloud()
|
||||
pcd.points = o3d.utility.Vector3dVector(vertices)
|
||||
|
||||
# print("voxel_size",voxel_size,obj_path, len(pcd.points), len(mesh_obj.vertices))
|
||||
|
||||
# 对点云进行下采样(体素网格法)
|
||||
#"""
|
||||
pcd_downsampled = down_sample(pcd, voxel_size)
|
||||
pcd_downsampled.paint_uniform_color([0, 0, 1])
|
||||
|
||||
if len(np.asarray(pcd_downsampled.points)) <= 0:
|
||||
bbox = pcd.get_axis_aligned_bounding_box()
|
||||
volume = bbox.volume()
|
||||
|
||||
# print(f"len(pcd.points)={len(pcd.points)}, volume={volume}")
|
||||
|
||||
# 处理体积为零的情况
|
||||
if volume <= 0:
|
||||
# 计算点云的实际范围
|
||||
points = np.asarray(pcd.points)
|
||||
if len(points) > 0:
|
||||
min_bound = np.min(points, axis=0)
|
||||
max_bound = np.max(points, axis=0)
|
||||
extent = max_bound - min_bound
|
||||
|
||||
# 确保最小维度至少为0.01
|
||||
min_dimension = max(0.01, np.min(extent))
|
||||
volume = min_dimension ** 3
|
||||
else:
|
||||
volume = 1.0 # 最后的安全回退
|
||||
|
||||
print(f"Warning: Zero volume detected, using approximated volume {volume:.6f} for {obj_path}")
|
||||
|
||||
# 安全计算密度 - 防止除零错误
|
||||
if len(pcd.points) > 0 and volume > 0:
|
||||
original_density = len(pcd.points) / volume
|
||||
voxel_size = max(0.01, min(10.0, 0.5 / (max(1e-6, original_density) ** 0.33)))
|
||||
else:
|
||||
# 当点数为0或体积为0时使用默认体素大小
|
||||
voxel_size = 1.0 # 默认值
|
||||
|
||||
print(f"Recalculated voxel_size: {voxel_size} for {obj_path}")
|
||||
|
||||
pcd_downsampled = down_sample(pcd, voxel_size)
|
||||
pcd_downsampled.paint_uniform_color([0, 0, 1])
|
||||
|
||||
original_num = len(pcd.points)
|
||||
target_samples = 1000
|
||||
num_samples = min(target_samples, original_num)
|
||||
|
||||
# print("get_lowest_position_of_center1 time", time.time()-start_time1)
|
||||
start_time2 = time.time()
|
||||
# 确保下采样后有点云
|
||||
if len(np.asarray(pcd_downsampled.points)) == 0:
|
||||
# 使用原始点云作为后备
|
||||
pcd_downsampled = pcd
|
||||
print(f"Warning: Using original point cloud for {obj_path} as downsampling produced no points")
|
||||
|
||||
points = np.asarray(pcd_downsampled.points)
|
||||
|
||||
# 初始化最小重心Y的值
|
||||
max_z_of_mass_y = float('inf')
|
||||
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
|
||||
best_angle_x, best_angle_y, best_angle_z, max_z_of_mass_y = parallel_rotation(points, angle_step=3)
|
||||
|
||||
# 使用最佳角度进行旋转并平移obj
|
||||
pcd_transformed = copy.deepcopy(mesh_obj)
|
||||
|
||||
# 最佳角度旋转
|
||||
R_x = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([1, 0, 0]) * np.radians(best_angle_x))
|
||||
pcd_transformed.rotate(R_x)
|
||||
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * np.radians(best_angle_y))
|
||||
pcd_transformed.rotate(R_y)
|
||||
R_z = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 0, 1]) * np.radians(best_angle_z))
|
||||
pcd_transformed.rotate(R_z)
|
||||
|
||||
T_x = np.eye(4)
|
||||
T_x[:3, :3] = R_x
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_x @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
T_y = np.eye(4)
|
||||
T_y[:3, :3] = R_y
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_y @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
T_z = np.eye(4)
|
||||
T_z[:3, :3] = R_z
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_z @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
#试着旋转180,让脸朝上
|
||||
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
# 计算平移向量,将最小Y值平移到0
|
||||
min_z = np.min(vertices[:, 2])
|
||||
translation_vector = np.array([0,0,-min_z,])
|
||||
pcd_transformed.translate(translation_vector)
|
||||
|
||||
T_trans1 = np.eye(4)
|
||||
T_trans1[:3, 3] = translation_vector
|
||||
total_matrix = T_trans1 @ total_matrix
|
||||
|
||||
# 计算 z 坐标均值
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
z_mean1 = np.mean(vertices[:, 2])
|
||||
z_max1 = np.max(vertices[:, 2])
|
||||
|
||||
volume_centroid = get_volume_centroid(pcd_transformed)
|
||||
z_volume_center1 = volume_centroid[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)
|
||||
pcd_transformed.translate(-center_point)
|
||||
pcd_transformed.rotate(R_y, center=(0, 0, 0))
|
||||
pcd_transformed.translate(center_point)
|
||||
|
||||
aabb = pcd_transformed.get_axis_aligned_bounding_box()
|
||||
# center_point = aabb.get_center()
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
# 构建绕中心点旋转的变换矩阵[3](@ref)
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
R_y180 = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * angle_rad)
|
||||
T_rotate = np.eye(4)
|
||||
T_rotate[:3, :3] = R_y180
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
T_rot_center = T_origin_to_center @ T_rotate @ T_center_to_origin
|
||||
total_matrix = T_rot_center @ total_matrix
|
||||
|
||||
#print("旋转后质心:", pcd_transformed.get_center())
|
||||
#print("旋转后点示例:", np.asarray(pcd_transformed.vertices)[:3])
|
||||
|
||||
#
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
# 计算平移向量,将最小Y值平移到0
|
||||
min_z = np.min(vertices[:, 2])
|
||||
max_z = np.max(vertices[:, 2])
|
||||
# print("min_z1", min_z, obj_path)
|
||||
translation_vector = np.array([0,0,-min_z,])
|
||||
# translation_vector = np.array([0,0,-min_z + (min_z-max_z),])
|
||||
# print("translation_vector1",translation_vector)
|
||||
pcd_transformed.translate(translation_vector)
|
||||
|
||||
T_trans2 = np.eye(4)
|
||||
T_trans2[:3, 3] = translation_vector
|
||||
translation = total_matrix[:3, 3]
|
||||
# print("translation_vector2",translation_vector)
|
||||
# print(1,translation)
|
||||
|
||||
total_matrix = T_trans2 @ total_matrix
|
||||
translation = total_matrix[:3, 3]
|
||||
# print(2,translation)
|
||||
|
||||
# 计算 z 坐标均值
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
z_mean2 = np.mean(vertices[:, 2])
|
||||
z_max2 = np.max(vertices[:, 2])
|
||||
volume_centroid = get_volume_centroid(pcd_transformed)
|
||||
z_volume_center2 = volume_centroid[2]
|
||||
|
||||
# print(f"get_lowest_position_of_center z_max1={z_max1}, z_max2={z_max2}, len={len(pcd_transformed.vertices)}, obj_path={obj_path}")
|
||||
|
||||
# print(f"z_mean1={z_mean1}, z_mean2={z_mean2}")
|
||||
# if (z_mean2 > z_mean1):
|
||||
# print(f"z_volume_center1={z_volume_center1}, z_volume_center2={z_volume_center2}")
|
||||
if (z_volume_center2 > z_volume_center1):
|
||||
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
|
||||
centroid = pcd_transformed.get_center()
|
||||
|
||||
aabb = pcd_transformed.get_axis_aligned_bounding_box()
|
||||
# center_point = aabb.get_center()
|
||||
center_point = compute_mesh_center(mesh_obj.vertices)
|
||||
|
||||
pcd_transformed.translate(-center_point)
|
||||
pcd_transformed.rotate(R_y, center=(0, 0, 0))
|
||||
pcd_transformed.translate(center_point)
|
||||
|
||||
T_center_to_origin = np.eye(4)
|
||||
T_center_to_origin[:3, 3] = -center_point
|
||||
T_origin_to_center = np.eye(4)
|
||||
T_origin_to_center[:3, 3] = center_point
|
||||
# 构建反向旋转矩阵
|
||||
R_y = pcd_transformed.get_rotation_matrix_from_axis_angle(np.array([0, 1, 0]) * -angle_rad)
|
||||
T_rotate_inv = np.eye(4)
|
||||
T_rotate_inv[:3, :3] = R_y
|
||||
# 完整的反向绕中心旋转矩阵
|
||||
T_rot_center_inv = T_origin_to_center @ T_rotate_inv @ T_center_to_origin
|
||||
total_matrix = T_rot_center_inv @ total_matrix
|
||||
|
||||
vertices = np.asarray(pcd_transformed.vertices)
|
||||
# 计算平移向量,将最小Y值平移到0
|
||||
min_z = np.min(vertices[:, 2])
|
||||
# print("min_z2", min_z, obj_path)
|
||||
translation_vector = np.array([0,0,-min_z,])
|
||||
pcd_transformed.translate(translation_vector)
|
||||
|
||||
T_trans3 = np.eye(4)
|
||||
T_trans3[:3, 3] = translation_vector
|
||||
total_matrix = T_trans3 @ total_matrix
|
||||
|
||||
# z_mean_min = min(z_mean1, z_mean2)
|
||||
z_max = min(z_max1, z_max2)
|
||||
|
||||
# print("get_lowest_position_of_center2 time", time.time()-start_time2)
|
||||
return total_matrix, z_max
|
||||
|
||||
def get_lowest_position_of_center_ext(mesh_obj, total_matrix):
|
||||
# print(f"get_lowest_position_of_center_ext mesh_obj={mesh_obj}")
|
||||
temp_matrix, z_max = get_lowest_position_of_z_ext(mesh_obj)
|
||||
|
||||
total_matrix = temp_matrix @ total_matrix
|
||||
|
||||
return total_matrix, z_max
|
||||
|
||||
def calculate_rotation_and_top_of_mass(angle_x, angle_y, angle_z, points):
|
||||
"""计算某一组旋转角度后的重心"""
|
||||
# 计算绕X轴、Y轴和Z轴的旋转矩阵
|
||||
R_x = np.array([
|
||||
[1, 0, 0],
|
||||
[0, np.cos(np.radians(angle_x)), -np.sin(np.radians(angle_x))],
|
||||
[0, np.sin(np.radians(angle_x)), np.cos(np.radians(angle_x))]
|
||||
])
|
||||
|
||||
R_y = np.array([
|
||||
[np.cos(np.radians(angle_y)), 0, np.sin(np.radians(angle_y))],
|
||||
[0, 1, 0],
|
||||
[-np.sin(np.radians(angle_y)), 0, np.cos(np.radians(angle_y))]
|
||||
])
|
||||
|
||||
R_z = np.array([
|
||||
[np.cos(np.radians(angle_z)), -np.sin(np.radians(angle_z)), 0],
|
||||
[np.sin(np.radians(angle_z)), np.cos(np.radians(angle_z)), 0],
|
||||
[0, 0, 1]
|
||||
])
|
||||
|
||||
# 综合旋转矩阵
|
||||
R = R_z @ R_y @ R_x
|
||||
|
||||
# 执行旋转
|
||||
rotated_points = points @ R.T
|
||||
|
||||
# 计算最小z值
|
||||
min_z = np.min(rotated_points[:, 2])
|
||||
|
||||
# 计算平移向量,将最小Z值平移到0
|
||||
translation_vector = np.array([0, 0, -min_z])
|
||||
rotated_points += translation_vector
|
||||
|
||||
top_of_mass = np.max(rotated_points, axis=0)
|
||||
|
||||
return top_of_mass[2], angle_x, angle_y, angle_z
|
||||
|
||||
def parallel_rotation(points, angle_step=4):
|
||||
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)"""
|
||||
max_top = float('inf')
|
||||
|
||||
for angle_x in range(-90, 90, angle_step):
|
||||
for angle_y in range(0, 360, angle_step):
|
||||
max_z, ax, ay, _ = calculate_rotation_and_top_of_mass(angle_x, angle_y, 0, points)
|
||||
if max_z < max_top:
|
||||
max_top = max_z
|
||||
best_angle_x = ax
|
||||
best_angle_y = ay
|
||||
|
||||
return (best_angle_x, best_angle_y, 0, max_top)
|
||||
|
||||
def compute_mesh_center(vertices):
|
||||
|
||||
if len(vertices) == 0:
|
||||
raise ValueError("顶点数组不能为空")
|
||||
|
||||
# 确保vertices是NumPy数组
|
||||
vertices_np = np.asarray(vertices)
|
||||
|
||||
# 使用NumPy的mean函数直接计算均值(向量化操作)
|
||||
centroid = np.mean(vertices_np, axis=0)
|
||||
|
||||
return centroid
|
||||
|
||||
# -------------------------- 结束:获取z值最低 --------------------------
|
||||
|
||||
# -------------------------- 开始:bbox --------------------------
|
||||
|
||||
def compute_bbox_ext(mesh_obj, obj_name="", is_downsample=True):
|
||||
|
||||
total_matrix = np.eye(4)
|
||||
total_matrix, z_max= get_lowest_position_of_center_ext(mesh_obj, total_matrix)
|
||||
|
||||
transformed_vertices = mesh_transform_by_matrix(np.asarray(mesh_obj.vertices), total_matrix)
|
||||
|
||||
obj_transformed = copy.deepcopy(mesh_obj)
|
||||
obj_transformed.vertices = o3d.utility.Vector3dVector(transformed_vertices)
|
||||
|
||||
voxel_size = 3 # 设置体素的大小,决定下采样的密度
|
||||
# 将点云摆正和X轴平衡
|
||||
obj_transformed_second,total_matrix = arrange_box_correctly(obj_transformed,voxel_size,total_matrix)
|
||||
|
||||
total_matrix, min_bound, max_bound, ply_name, pcd_fix = get_new_bbox(obj_transformed_second,obj_name,voxel_size,is_downsample,total_matrix)
|
||||
|
||||
del obj_transformed
|
||||
del obj_transformed_second
|
||||
|
||||
# return total_matrix, z_max, min_bound, max_bound, ply_name, pcd_fix
|
||||
return total_matrix, z_max, min_bound, max_bound, ply_name
|
||||
|
||||
def arrange_box_correctly(obj_transformed, voxel_size,total_matrix):
|
||||
|
||||
vertices = np.asarray(obj_transformed.vertices)
|
||||
pcd = o3d.geometry.PointCloud()
|
||||
pcd.points = o3d.utility.Vector3dVector(vertices)
|
||||
|
||||
# 降采样与特征计算
|
||||
# pcd_downsampled = down_sample(pcd, voxel_size)
|
||||
# points = np.asarray(pcd_downsampled.points)
|
||||
points = np.asarray(pcd.points)
|
||||
|
||||
cov = np.cov(points.T)
|
||||
|
||||
center = obj_transformed.get_center()
|
||||
|
||||
eigen_vals, eigen_vecs = np.linalg.eigh(cov)
|
||||
max_axis = eigen_vecs[:, np.argmax(eigen_vals)]
|
||||
|
||||
# print("max_axis", max_axis)
|
||||
# 强制主方向向量X分量为正(指向右侧)
|
||||
if max_axis[0] < 0 or (max_axis[0] == 0 and max_axis[1] < 0):
|
||||
max_axis = -max_axis
|
||||
|
||||
target_dir = np.array([1, 0]) # 目标方向为X正轴
|
||||
current_dir = max_axis[:2] / np.linalg.norm(max_axis[:2])
|
||||
dot_product = np.dot(current_dir, target_dir)
|
||||
|
||||
# print("dot_product", dot_product)
|
||||
if dot_product < 0.8: # 阈值控制方向敏感性(建议0.6~0.9)
|
||||
max_axis = -max_axis # 强制翻转方向
|
||||
|
||||
# 计算旋转角度
|
||||
angle_z = np.arctan2(max_axis[1], max_axis[0]) % (2 * np.pi)
|
||||
|
||||
if max_axis[0] <= 0 and max_axis[1] <= 0:
|
||||
angle_z += np.pi
|
||||
|
||||
R = o3d.geometry.get_rotation_matrix_from_axis_angle([0, 0, -angle_z])
|
||||
|
||||
T = np.eye(4)
|
||||
T[:3, :3] = R
|
||||
T[:3, 3] = center - R.dot(center) # 保持中心不变
|
||||
obj_transformed.transform(T)
|
||||
|
||||
total_matrix = T @ total_matrix
|
||||
|
||||
return obj_transformed, total_matrix
|
||||
|
||||
def get_new_bbox(obj_transformed_second,obj_name,voxel_size,is_downsample,total_matrix):
|
||||
|
||||
# 计算点云的边界
|
||||
points = np.asarray(obj_transformed_second.vertices)
|
||||
|
||||
min_bound = np.min(points, axis=0) # 获取点云的最小边界
|
||||
max_bound = np.max(points, axis=0) # 获取点云的最大边界
|
||||
|
||||
# print(f"get_new_bbox1: obj_name={obj_name}, min_bound={min_bound}, max_bound={max_bound}")
|
||||
|
||||
# 确保包围盒的Y坐标不低于0
|
||||
min_bound[2] = max(min_bound[2], 0) # 确保Y坐标的最小值不低于0
|
||||
|
||||
# 重新计算包围盒的中心和半长轴
|
||||
bbox_center = (min_bound + max_bound) / 2 # 计算包围盒的中心点
|
||||
bbox_extent = (max_bound - min_bound) # 计算包围盒的半长轴(尺寸)
|
||||
|
||||
# 创建包围盒,确保尺寸正确
|
||||
new_bbox = o3d.geometry.OrientedBoundingBox(center=bbox_center,
|
||||
R=np.eye(3), # 旋转矩阵,默认没有旋转
|
||||
extent=bbox_extent)
|
||||
# 获取包围盒的长、宽和高
|
||||
x_length = round(bbox_extent[0],3) # X 方向的长
|
||||
y_length = round(bbox_extent[1],3) # Y 方向的宽
|
||||
z_length = round(bbox_extent[2],3) # Z 方向的高
|
||||
bbox_points = np.array([
|
||||
[min_bound[0], min_bound[1], min_bound[2]],
|
||||
[max_bound[0], min_bound[1], min_bound[2]],
|
||||
[max_bound[0], max_bound[1], min_bound[2]],
|
||||
[min_bound[0], max_bound[1], min_bound[2]],
|
||||
[min_bound[0], min_bound[1], max_bound[2]],
|
||||
[max_bound[0], min_bound[1], max_bound[2]],
|
||||
[max_bound[0], max_bound[1], max_bound[2]],
|
||||
[min_bound[0], max_bound[1], max_bound[2]]
|
||||
])
|
||||
first_corner = bbox_points[2]
|
||||
translation_vector = -first_corner
|
||||
obj_transformed_second.translate(translation_vector)
|
||||
|
||||
T_trans = np.eye(4)
|
||||
T_trans[:3, 3] = translation_vector # 设置平移分量 [2,3](@ref)
|
||||
total_matrix = T_trans @ total_matrix # 矩阵乘法顺序:最新变换左乘[4,5](@ref)
|
||||
|
||||
new_bbox.translate(translation_vector)
|
||||
|
||||
vertices = np.asarray(obj_transformed_second.vertices)
|
||||
pcd = o3d.geometry.PointCloud()
|
||||
pcd.points = o3d.utility.Vector3dVector(vertices)
|
||||
if is_downsample:
|
||||
pcd_downsampled = down_sample(pcd, voxel_size, False)
|
||||
pcd_fix = pcd_downsampled
|
||||
else:
|
||||
pcd_fix = pcd
|
||||
|
||||
min_bound = np.min(pcd.points, axis=0) # 获取点云的最小边界
|
||||
max_bound = np.max(pcd.points, axis=0) # 获取点云的最大边界
|
||||
# print(f"get_new_bbox2: min_bound={min_bound}, max_bound={max_bound}")
|
||||
|
||||
if (not obj_name == ""):
|
||||
ply_print_pid = obj_name.replace(".obj","")
|
||||
ply_name = f"{ply_print_pid}={z_length}+{y_length}+{x_length}.ply"
|
||||
else:
|
||||
ply_name = ""
|
||||
|
||||
return total_matrix, min_bound, max_bound, ply_name, pcd_fix
|
||||
|
||||
# -------------------------- 结束:bbox --------------------------
|
||||
|
||||
# -------------------------- 开始:general --------------------------
|
||||
|
||||
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 read_mesh(obj_path, enable_post_processing=False):
|
||||
mesh_obj = o3d.io.read_triangle_mesh(obj_path, enable_post_processing)
|
||||
return mesh_obj
|
||||
|
||||
def mesh_transform_by_matrix(vertices, transform_matrix):
|
||||
"""
|
||||
手动实现网格变换:对每个顶点应用齐次变换矩阵
|
||||
参数:
|
||||
vertices: 网格顶点数组 (N, 3)
|
||||
transform_matrix: 4x4 齐次变换矩阵
|
||||
返回:
|
||||
变换后的顶点数组 (N, 3)
|
||||
"""
|
||||
# 1. 顶点转齐次坐标 (N, 3) → (N, 4)
|
||||
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1))))
|
||||
|
||||
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN)
|
||||
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T
|
||||
|
||||
# 3. 转回非齐次坐标 (3xN) → (N, 3)
|
||||
transformed_vertices = transformed_homogeneous[:3, :].T
|
||||
|
||||
del homogeneous_vertices
|
||||
del transformed_homogeneous
|
||||
|
||||
return transformed_vertices
|
||||
|
||||
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)
|
||||
|
||||
voxel_size = 3
|
||||
|
||||
# -------------------------- 结束:general --------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--obj_path", type=str, required=True, help="obj 路径")
|
||||
args = parser.parse_args()
|
||||
|
||||
obj_path = args.obj_path
|
||||
|
||||
total_matrix, z_max, min_bound, max_bound, ply_name = compute_bbox_out(obj_path)
|
||||
print(f"total_matrix={total_matrix}, z_max={z_max}, min_bound={min_bound}, max_bound={max_bound}, ply_name={ply_name}")
|
||||
|
||||
|
||||
@@ -48,8 +48,8 @@ small_machine_size = [380, 345, 250]
|
||||
# -------------------------- 开始:测试相关 -------------------------------
|
||||
|
||||
# local_data= {'machine_print_counts': 150, 'machine_type': 'big_machine', 'pre_batch_id': 9910032, 'print_machine_id': 14, 'print_orders': [{'counts': 1, 'layout_z': 35.62070959151145, 'model_weight': 41.862, 'order_id': 917900, 'pid': 376666, 'print_id': 126318}, {'counts': 1, 'layout_z': 31.380497732946978, 'model_weight': 35.158, 'order_id': 917810, 'pid': 376562, 'print_id': 126413}, {'counts': 1, 'layout_z': 26.136854969886777, 'model_weight': 21.403, 'order_id': 917820, 'pid': 376572, 'print_id': 126414}, {'counts': 1, 'layout_z': 37.967177899738104, 'model_weight': 30.48, 'order_id': 917922, 'pid': 376684, 'print_id': 126415}, {'counts': 1, 'layout_z': 20.368887098446734, 'model_weight': 13.097, 'order_id': 918228, 'pid': 377198, 'print_id': 126417}, {'counts': 1, 'layout_z': 21.4227336927951, 'model_weight': 15.859, 'order_id': 917576, 'pid': 376345, 'print_id': 126769}, {'counts': 1, 'layout_z': 27.539947066784066, 'model_weight': 25.185, 'order_id': 917811, 'pid': 376564, 'print_id': 126875}, {'counts': 1, 'layout_z': 31.692723822970393, 'model_weight': 32.796, 'order_id': 917821, 'pid': 376592, 'print_id': 126876}, {'counts': 1, 'layout_z': 20.20031720146163, 'model_weight': 8.664, 'order_id': 917822, 'pid': 376602, 'print_id': 126877}, {'counts': 1, 'layout_z': 22.41680629925537, 'model_weight': 15.206, 'order_id': 917827, 'pid': 376616, 'print_id': 126878}, {'counts': 1, 'layout_z': 25.443981671314514, 'model_weight': 11.965, 'order_id': 917879, 'pid': 376635, 'print_id': 126879}, {'counts': 1, 'layout_z': 31.09222672704002, 'model_weight': 20.318, 'order_id': 917902, 'pid': 376667, 'print_id': 126882}, {'counts': 1, 'layout_z': 30.3317820592175, 'model_weight': 32.964, 'order_id': 917903, 'pid': 376668, 'print_id': 126884}, {'counts': 1, 'layout_z': 28.69639626624317, 'model_weight': 27.751, 'order_id': 917904, 'pid': 376669, 'print_id': 126885}, {'counts': 1, 'layout_z': 28.043180029163203, 'model_weight': 19.866, 'order_id': 917917, 'pid': 376677, 'print_id': 126886}, {'counts': 1, 'layout_z': 28.58783468947201, 'model_weight': 25.505, 'order_id': 918196, 'pid': 377126, 'print_id': 126888}, {'counts': 1, 'layout_z': 43.55871899466554, 'model_weight': 63.512, 'order_id': 918201, 'pid': 377131, 'print_id': 126890}, {'counts': 1, 'layout_z': 32.52479624889478, 'model_weight': 22.943, 'order_id': 918203, 'pid': 377133, 'print_id': 126891}, {'counts': 1, 'layout_z': 33.21834123013511, 'model_weight': 42.163, 'order_id': 918206, 'pid': 377139, 'print_id': 126892}, {'counts': 1, 'layout_z': 27.81674335447885, 'model_weight': 15.455, 'order_id': 918207, 'pid': 377141, 'print_id': 126893}, {'counts': 1, 'layout_z': 29.78203639784899, 'model_weight': 29.696, 'order_id': 918231, 'pid': 377201, 'print_id': 126894}, {'counts': 1, 'layout_z': 23.66875736859606, 'model_weight': 13.39, 'order_id': 915637, 'pid': 373135, 'print_id': 126895}, {'counts': 1, 'layout_z': 29.00408914521088, 'model_weight': 19.553, 'order_id': 918246, 'pid': 377209, 'print_id': 126897}, {'counts': 1, 'layout_z': 24.44397251338635, 'model_weight': 15.424, 'order_id': 918325, 'pid': 376974, 'print_id': 126899}, {'counts': 1, 'layout_z': 31.755406379699707, 'model_weight': 31.131, 'order_id': 918492, 'pid': 377584, 'print_id': 126900}, {'counts': 1, 'layout_z': 38.03699946264237, 'model_weight': 47.412, 'order_id': 918493, 'pid': 377587, 'print_id': 126901}, {'counts': 1, 'layout_z': 43.52919799223749, 'model_weight': 37.842, 'order_id': 918494, 'pid': 377589, 'print_id': 126902}, {'counts': 1, 'layout_z': 25.906302369108843, 'model_weight': 18.64, 'order_id': 918495, 'pid': 377590, 'print_id': 126903}, {'counts': 1, 'layout_z': 36.02611978829816, 'model_weight': 43.814, 'order_id': 918496, 'pid': 377591, 'print_id': 126904}, {'counts': 1, 'layout_z': 26.020689766262052, 'model_weight': 18.513, 'order_id': 918497, 'pid': 377594, 'print_id': 126905}, {'counts': 1, 'layout_z': 23.988078117370605, 'model_weight': 15.753, 'order_id': 918498, 'pid': 377596, 'print_id': 126906}, {'counts': 1, 'layout_z': 27.457814063914604, 'model_weight': 23.507, 'order_id': 918500, 'pid': 377601, 'print_id': 126908}, {'counts': 1, 'layout_z': 29.306090320672993, 'model_weight': 25.043, 'order_id': 918501, 'pid': 377605, 'print_id': 126909}, {'counts': 1, 'layout_z': 29.070231478924583, 'model_weight': 25.598, 'order_id': 918504, 'pid': 377606, 'print_id': 126910}, {'counts': 1, 'layout_z': 24.314504768436095, 'model_weight': 18.68, 'order_id': 918555, 'pid': 377684, 'print_id': 126911}, {'counts': 1, 'layout_z': 37.52979171030315, 'model_weight': 53.541, 'order_id': 918558, 'pid': 377689, 'print_id': 126913}, {'counts': 1, 'layout_z': 26.530791960490333, 'model_weight': 15.082, 'order_id': 918560, 'pid': 377702, 'print_id': 126915}, {'counts': 1, 'layout_z': 23.604568481445312, 'model_weight': 21.614, 'order_id': 918583, 'pid': 377748, 'print_id': 126917}, {'counts': 1, 'layout_z': 27.865868478825355, 'model_weight': 22.6, 'order_id': 918636, 'pid': 37Line truncated
|
||||
# local_data= {"machine_print_counts":50, 'machine_type': 'small_machine',"pre_batch_id":991112,"print_machine_id":22,"print_orders":[{"counts":1,"layout_z":3.543965775898459,"model_weight":12.833,"order_id":857420,"pid":268473,"print_id":85240},{"counts":1,"layout_z":7.798861189952786,"model_weight":13.784,"order_id":875986,"pid":305425,"print_id":88136}]}
|
||||
local_data= {'machine_print_counts': 130, 'machine_type': 'big_machine', 'pre_batch_id': 9910188, 'print_machine_id': 15, 'print_orders': [{'counts': 1, 'layout_z': 32.65071311811036, 'model_weight': 68.084, 'order_id': 926400, 'pid': 389256, 'print_id': 134168}, {'counts': 1, 'layout_z': 31.780598561603746, 'model_weight': 25.511, 'order_id': 926573, 'pid': 389465, 'print_id': 134170}, {'counts': 1, 'layout_z': 5.202356934547428, 'model_weight': 11.705, 'order_id': 925909, 'pid': 388682, 'print_id': 134178}, {'counts': 1, 'layout_z': 5.1447470188140905, 'model_weight': 12.186, 'order_id': 925910, 'pid': 388683, 'print_id': 134179}, {'counts': 1, 'layout_z': 5.013986945152283, 'model_weight': 11.364, 'order_id': 925932, 'pid': 388707, 'print_id': 134180}, {'counts': 1, 'layout_z': 5.020000934600831, 'model_weight': 11.396, 'order_id': 926770, 'pid': 389842, 'print_id': 134183}, {'counts': 1, 'layout_z': 40.05588230441501, 'model_weight': 60.578, 'order_id': 926776, 'pid': 389895, 'print_id': 134185}, {'counts': 1, 'layout_z': 46.23669371110479, 'model_weight': 56.196, 'order_id': 926778, 'pid': 389918, 'print_id': 134186}, {'counts': 1, 'layout_z': 24.822566986083984, 'model_weight': 31.522, 'order_id': 926268, 'pid': 389067, 'print_id': 134211}, {'counts': 1, 'layout_z': 25.085425667575343, 'model_weight': 31.077, 'order_id': 926267, 'pid': 389066, 'print_id': 134212}, {'counts': 1, 'layout_z': 18.154759649173364, 'model_weight': 6.256, 'order_id': 927059, 'pid': 390269, 'print_id': 134216}, {'counts': 1, 'layout_z': 24.2420189021486, 'model_weight': 14.472, 'order_id': 927065, 'pid': 390275, 'print_id': 134220}, {'counts': 1, 'layout_z': 16.757099114319693, 'model_weight': 5.335, 'order_id': 927078, 'pid': 390299, 'print_id': 134226}, {'counts': 1, 'layout_z': 18.796780948141503, 'model_weight': 7.295, 'order_id': 927079, 'pid': 390300, 'print_id': 134228}, {'counts': 1, 'layout_z': 17.920740666248633, 'model_weight': 7.869, 'order_id': 927082, 'pid': 390303, 'print_id': 134229}, {'counts': 1, 'layout_z': 16.320665859825066, 'model_weight': 4.73, 'order_id': 927081, 'pid': 390302, 'print_id': 134230}, {'counts': 1, 'layout_z': 23.250497020255544, 'model_weight': 15.567, 'order_id': 927100, 'pid': 390331, 'print_id': 134242}, {'counts': 1, 'layout_z': 26.276874511435977, 'model_weight': 19.813, 'order_id': 927103, 'pid': 390334, 'print_id': 134246}, {'counts': 1, 'layout_z': 17.08049576865031, 'model_weight': 5.858, 'order_id': 927111, 'pid': 390342, 'print_id': 134254}, {'counts': 1, 'layout_z': 19.196685578562278, 'model_weight': 6.465, 'order_id': 927117, 'pid': 390347, 'print_id': 134261}, {'counts': 1, 'layout_z': 26.507062216602122, 'model_weight': 14.287, 'order_id': 927116, 'pid': 390346, 'print_id': 134262}, {'counts': 1, 'layout_z': 16.440782827635232, 'model_weight': 5.493, 'order_id': 927118, 'pid': 390348, 'print_id': 134263}, {'counts': 1, 'layout_z': 41.42868301509347, 'model_weight': 68.274, 'order_id': 927122, 'pid': 390354, 'print_id': 134264}, {'counts': 1, 'layout_z': 15.705368176518945, 'model_weight': 4.423, 'order_id': 927119, 'pid': 390349, 'print_id': 134265}, {'counts': 1, 'layout_z': 16.363223697461702, 'model_weight': 5.634, 'order_id': 927120, 'pid': 390351, 'print_id': 134266}, {'counts': 1, 'layout_z': 24.653417686230497, 'model_weight': 17.851, 'order_id': 927121, 'pid': 390353, 'print_id': 134267}, {'counts': 1, 'layout_z': 24.327745611835496, 'model_weight': 28.23, 'order_id': 926040, 'pid': 388832, 'print_id': 134268}, {'counts': 1, 'layout_z': 27.301779030909188, 'model_weight': 23.926, 'order_id': 923281, 'pid': 385001, 'print_id': 134269}, {'counts': 1, 'layout_z': 25.869120288866796, 'model_weight': 32.329, 'order_id': 926077, 'pid': 388864, 'print_id': 134270}, {'counts': 1, 'layout_z': 36.37214874852985, 'model_weight': 65.744, 'order_id': 926086, 'pid': 388873, 'print_id': 134271}, {'counts': 1, 'layout_z': 37.44403689090713, 'model_weight': 80.67, 'order_id': 926097, 'pid': 388884, 'print_id': 134273}, {'counts': 1, 'layout_z': 27.890566642299536, 'model_weight': 22.715, 'order_id': 927127, 'pid': 390372, 'print_id': 134274}, {'counts': 1, 'layout_z': 17.392635113631542, 'model_weight': 5.297, 'order_id': 927128, 'pid': 390373, 'print_id': 134275}, {'counts': 1, 'layout_z': 26.139986312446986, 'model_weight': 15.312, 'order_id': 927129, 'pid': 390375, 'print_id': 134276}, {'counts': 1, 'layout_z': 17.491576609126483, 'model_weight': 6.495, 'order_id': 927130, 'pid': 390376, 'print_id': 134277}, {'counts': 1, 'layout_z': 15.57976295844962, 'model_weight': 5.443, 'order_id': 927131, 'pid': 390377, 'print_id': 134278}, {'counts': 1, 'layout_z': 16.006999969482422, 'model_weight': 4.683, 'order_id': 927132, 'pid': 390378, 'print_id': 134279}, {'counts': 1, 'layout_z': 18.770485206437296, 'model_weight': 6.114, 'order_id': 927137, 'pid': 390383, 'print_id': 134280}, {'counts': 1, 'layout_z': 21.480148458997267, 'model_weight': 9.586, 'order_id': 927133, 'pid': 390379,Line truncated
|
||||
local_data= {"machine_print_counts":50, 'machine_type': 'small_machine',"pre_batch_id":991112,"print_machine_id":22,"print_orders":[{"counts":1,"layout_z":3.543965775898459,"model_weight":12.833,"order_id":857420,"pid":268473,"print_id":85240},{"counts":1,"layout_z":7.798861189952786,"model_weight":13.784,"order_id":875986,"pid":305425,"print_id":88136}]}
|
||||
# local_data= {'machine_print_counts': 130, 'machine_type': 'big_machine', 'pre_batch_id': 9910188, 'print_machine_id': 15, 'print_orders': [{'counts': 1, 'layout_z': 32.65071311811036, 'model_weight': 68.084, 'order_id': 926400, 'pid': 389256, 'print_id': 134168}, {'counts': 1, 'layout_z': 31.780598561603746, 'model_weight': 25.511, 'order_id': 926573, 'pid': 389465, 'print_id': 134170}, {'counts': 1, 'layout_z': 5.202356934547428, 'model_weight': 11.705, 'order_id': 925909, 'pid': 388682, 'print_id': 134178}, {'counts': 1, 'layout_z': 5.1447470188140905, 'model_weight': 12.186, 'order_id': 925910, 'pid': 388683, 'print_id': 134179}, {'counts': 1, 'layout_z': 5.013986945152283, 'model_weight': 11.364, 'order_id': 925932, 'pid': 388707, 'print_id': 134180}, {'counts': 1, 'layout_z': 5.020000934600831, 'model_weight': 11.396, 'order_id': 926770, 'pid': 389842, 'print_id': 134183}, {'counts': 1, 'layout_z': 40.05588230441501, 'model_weight': 60.578, 'order_id': 926776, 'pid': 389895, 'print_id': 134185}, {'counts': 1, 'layout_z': 46.23669371110479, 'model_weight': 56.196, 'order_id': 926778, 'pid': 389918, 'print_id': 134186}, {'counts': 1, 'layout_z': 24.822566986083984, 'model_weight': 31.522, 'order_id': 926268, 'pid': 389067, 'print_id': 134211}, {'counts': 1, 'layout_z': 25.085425667575343, 'model_weight': 31.077, 'order_id': 926267, 'pid': 389066, 'print_id': 134212}, {'counts': 1, 'layout_z': 18.154759649173364, 'model_weight': 6.256, 'order_id': 927059, 'pid': 390269, 'print_id': 134216}, {'counts': 1, 'layout_z': 24.2420189021486, 'model_weight': 14.472, 'order_id': 927065, 'pid': 390275, 'print_id': 134220}, {'counts': 1, 'layout_z': 16.757099114319693, 'model_weight': 5.335, 'order_id': 927078, 'pid': 390299, 'print_id': 134226}, {'counts': 1, 'layout_z': 18.796780948141503, 'model_weight': 7.295, 'order_id': 927079, 'pid': 390300, 'print_id': 134228}, {'counts': 1, 'layout_z': 17.920740666248633, 'model_weight': 7.869, 'order_id': 927082, 'pid': 390303, 'print_id': 134229}, {'counts': 1, 'layout_z': 16.320665859825066, 'model_weight': 4.73, 'order_id': 927081, 'pid': 390302, 'print_id': 134230}, {'counts': 1, 'layout_z': 23.250497020255544, 'model_weight': 15.567, 'order_id': 927100, 'pid': 390331, 'print_id': 134242}, {'counts': 1, 'layout_z': 26.276874511435977, 'model_weight': 19.813, 'order_id': 927103, 'pid': 390334, 'print_id': 134246}, {'counts': 1, 'layout_z': 17.08049576865031, 'model_weight': 5.858, 'order_id': 927111, 'pid': 390342, 'print_id': 134254}, {'counts': 1, 'layout_z': 19.196685578562278, 'model_weight': 6.465, 'order_id': 927117, 'pid': 390347, 'print_id': 134261}, {'counts': 1, 'layout_z': 26.507062216602122, 'model_weight': 14.287, 'order_id': 927116, 'pid': 390346, 'print_id': 134262}, {'counts': 1, 'layout_z': 16.440782827635232, 'model_weight': 5.493, 'order_id': 927118, 'pid': 390348, 'print_id': 134263}, {'counts': 1, 'layout_z': 41.42868301509347, 'model_weight': 68.274, 'order_id': 927122, 'pid': 390354, 'print_id': 134264}, {'counts': 1, 'layout_z': 15.705368176518945, 'model_weight': 4.423, 'order_id': 927119, 'pid': 390349, 'print_id': 134265}, {'counts': 1, 'layout_z': 16.363223697461702, 'model_weight': 5.634, 'order_id': 927120, 'pid': 390351, 'print_id': 134266}, {'counts': 1, 'layout_z': 24.653417686230497, 'model_weight': 17.851, 'order_id': 927121, 'pid': 390353, 'print_id': 134267}, {'counts': 1, 'layout_z': 24.327745611835496, 'model_weight': 28.23, 'order_id': 926040, 'pid': 388832, 'print_id': 134268}, {'counts': 1, 'layout_z': 27.301779030909188, 'model_weight': 23.926, 'order_id': 923281, 'pid': 385001, 'print_id': 134269}, {'counts': 1, 'layout_z': 25.869120288866796, 'model_weight': 32.329, 'order_id': 926077, 'pid': 388864, 'print_id': 134270}, {'counts': 1, 'layout_z': 36.37214874852985, 'model_weight': 65.744, 'order_id': 926086, 'pid': 388873, 'print_id': 134271}, {'counts': 1, 'layout_z': 37.44403689090713, 'model_weight': 80.67, 'order_id': 926097, 'pid': 388884, 'print_id': 134273}, {'counts': 1, 'layout_z': 27.890566642299536, 'model_weight': 22.715, 'order_id': 927127, 'pid': 390372, 'print_id': 134274}, {'counts': 1, 'layout_z': 17.392635113631542, 'model_weight': 5.297, 'order_id': 927128, 'pid': 390373, 'print_id': 134275}, {'counts': 1, 'layout_z': 26.139986312446986, 'model_weight': 15.312, 'order_id': 927129, 'pid': 390375, 'print_id': 134276}, {'counts': 1, 'layout_z': 17.491576609126483, 'model_weight': 6.495, 'order_id': 927130, 'pid': 390376, 'print_id': 134277}, {'counts': 1, 'layout_z': 15.57976295844962, 'model_weight': 5.443, 'order_id': 927131, 'pid': 390377, 'print_id': 134278}, {'counts': 1, 'layout_z': 16.006999969482422, 'model_weight': 4.683, 'order_id': 927132, 'pid': 390378, 'print_id': 134279}, {'counts': 1, 'layout_z': 18.770485206437296, 'model_weight': 6.114, 'order_id': 927137, 'pid': 390383, 'print_id': 134280}, {'counts': 1, 'layout_z': 21.480148458997267, 'model_weight': 9.586, 'order_id': 927133, 'pid': 39037Line truncated
|
||||
|
||||
test_print_max = 0
|
||||
|
||||
|
||||
+4
-83
@@ -3,6 +3,10 @@ import numpy as np
|
||||
import re
|
||||
from config import *
|
||||
|
||||
from compute_print_net_out import mesh_transform_by_matrix
|
||||
from compute_print_net_out import down_sample
|
||||
from compute_print_net_out import voxel_size
|
||||
|
||||
# -------------------------- 开始:运行 ----------------------------------
|
||||
|
||||
def is_run_local_data():
|
||||
@@ -23,64 +27,6 @@ def is_use_debug_oss():
|
||||
# -------------------------- 结束:运行 ----------------------------------
|
||||
|
||||
# -------------------------- 开始:模型 ----------------------------------
|
||||
|
||||
def read_mesh(obj_path, enable_post_processing=False):
|
||||
mesh_obj = o3d.io.read_triangle_mesh(obj_path, enable_post_processing)
|
||||
return mesh_obj
|
||||
|
||||
def down_sample(pcd, voxel_size, farthest_sample = False):
|
||||
original_num = len(pcd.points)
|
||||
target_samples = 1500 # 1000
|
||||
num_samples = min(target_samples, original_num)
|
||||
|
||||
# 第一步:使用体素下采样快速减少点数量
|
||||
# voxel_size = 3
|
||||
if farthest_sample:
|
||||
pcd_voxel = pcd.farthest_point_down_sample(num_samples=num_samples)
|
||||
else:
|
||||
pcd_voxel = pcd.voxel_down_sample(voxel_size)
|
||||
down_num = len(pcd_voxel.points)
|
||||
# print(f"original_num={original_num}, down_num={down_num}")
|
||||
|
||||
# 第二步:仅在必要时进行最远点下采样
|
||||
if len(pcd_voxel.points) > target_samples and False:
|
||||
pcd_downsampled = pcd_voxel.farthest_point_down_sample(num_samples=num_samples)
|
||||
else:
|
||||
pcd_downsampled = pcd_voxel
|
||||
|
||||
return pcd_downsampled
|
||||
|
||||
def get_volume_centroid(mesh):
|
||||
"""向量化版本的体积重心计算"""
|
||||
vertices = np.asarray(mesh.vertices)
|
||||
triangles = np.asarray(mesh.triangles)
|
||||
|
||||
# 一次性获取所有三角形的顶点 [n_triangles, 3, 3]
|
||||
tri_vertices = vertices[triangles]
|
||||
|
||||
# 分别提取三个顶点
|
||||
v0 = tri_vertices[:, 0, :] # [n, 3]
|
||||
v1 = tri_vertices[:, 1, :] # [n, 3]
|
||||
v2 = tri_vertices[:, 2, :] # [n, 3]
|
||||
|
||||
# 向量化计算叉积和点积
|
||||
cross_vec = np.cross(v1, v2) # [n, 3]
|
||||
dot_results = np.einsum('ij,ij->i', v0, cross_vec) # 批量点积 [n]
|
||||
|
||||
# 计算每个四面体体积 [n]
|
||||
tetra_volumes = np.abs(dot_results) / 6.0
|
||||
|
||||
# 计算每个四面体重心 [n, 3]
|
||||
tetra_centers = (v0 + v1 + v2) / 4.0
|
||||
|
||||
# 总体积和加权重心
|
||||
total_volume = np.sum(tetra_volumes)
|
||||
|
||||
if total_volume > 0:
|
||||
weighted_center = np.sum(tetra_volumes[:, np.newaxis] * tetra_centers, axis=0) / total_volume
|
||||
return weighted_center
|
||||
else:
|
||||
return np.mean(vertices, axis=0)
|
||||
|
||||
def mesh_tranform_to_pcd(mesh, transform_matrix):
|
||||
vertices = np.asarray(mesh.vertices)
|
||||
@@ -101,29 +47,6 @@ def mesh_to_pcd(mesh, is_down_sample=True):
|
||||
return pcd_downsampled
|
||||
else:
|
||||
return pcd
|
||||
|
||||
def mesh_transform_by_matrix(vertices, transform_matrix):
|
||||
"""
|
||||
手动实现网格变换:对每个顶点应用齐次变换矩阵
|
||||
参数:
|
||||
vertices: 网格顶点数组 (N, 3)
|
||||
transform_matrix: 4x4 齐次变换矩阵
|
||||
返回:
|
||||
变换后的顶点数组 (N, 3)
|
||||
"""
|
||||
# 1. 顶点转齐次坐标 (N, 3) → (N, 4)
|
||||
homogeneous_vertices = np.hstack((vertices, np.ones((vertices.shape[0], 1))))
|
||||
|
||||
# 2. 应用变换矩阵:矩阵乘法 (4x4) * (4xN) → (4xN)
|
||||
transformed_homogeneous = transform_matrix @ homogeneous_vertices.T
|
||||
|
||||
# 3. 转回非齐次坐标 (3xN) → (N, 3)
|
||||
transformed_vertices = transformed_homogeneous[:3, :].T
|
||||
|
||||
del homogeneous_vertices
|
||||
del transformed_homogeneous
|
||||
|
||||
return transformed_vertices
|
||||
|
||||
import os
|
||||
|
||||
@@ -240,8 +163,6 @@ def transform_save_o3d(layout_data, original_obj_pid_dir):
|
||||
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)
|
||||
|
||||
@@ -11,12 +11,13 @@ from plyfile import PlyData, PlyElement
|
||||
|
||||
from general import mesh_tranform_to_pcd
|
||||
from general import need_upload_result
|
||||
from general import read_mesh
|
||||
from general import extend_dist_min_collision
|
||||
|
||||
from compute_print_net import arrange_models_on_platform
|
||||
from compute_print_net import compute_bbox_all
|
||||
|
||||
from compute_print_net_out import read_mesh
|
||||
|
||||
def make_bbox_for_print(base_original_obj_dir,dict_bad,dict_origin,is_downsample):
|
||||
"""获取需要的盒子大小"""
|
||||
start_time = time.time()
|
||||
@@ -61,6 +62,7 @@ def get_dict_pcd(dict_mesh_obj,dict_total_matrix,all_models):
|
||||
dict_ply_name = {}
|
||||
for model in all_models:
|
||||
ply_name = model['name']
|
||||
# print(f"get_dict_pcd {ply_name.split("=")[0]}.obj, {ply_name}")
|
||||
dict_ply_name[f"{ply_name.split("=")[0]}.obj"] = ply_name
|
||||
|
||||
dict_pcd_fix = get_pcd_by_matrix(dict_mesh_obj,dict_total_matrix,dict_ply_name)
|
||||
|
||||
@@ -17,7 +17,7 @@ from multiprocessing import Pool, RawArray
|
||||
import ctypes
|
||||
import itertools
|
||||
|
||||
from compute_print_net import down_sample
|
||||
from compute_print_net_out import down_sample
|
||||
|
||||
def make_pcd_plane():
|
||||
# 创建Y=0的平面点云
|
||||
@@ -80,57 +80,6 @@ def calculate_rotation_and_center_of_mass(angle_x, angle_y, angle_z, points):
|
||||
|
||||
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
|
||||
#
|
||||
# # 计算每个角度的组合
|
||||
# angles_x = range(0, 360, angle_step)
|
||||
# angles_y = range(0, 360, angle_step)
|
||||
# angles_z = range(0, 360, angle_step)
|
||||
#
|
||||
# # 创建一个进程池并行处理
|
||||
# with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
|
||||
# results = []
|
||||
#
|
||||
# # 提交任务
|
||||
# for angle_x in angles_x:
|
||||
# for angle_y in angles_y:
|
||||
# for angle_z in angles_z:
|
||||
# results.append(
|
||||
# pool.apply_async(calculate_rotation_and_center_of_mass, (angle_x, angle_y, angle_z, points)))
|
||||
#
|
||||
# # 获取所有结果
|
||||
# for result in results:
|
||||
# center_of_mass_z, angle_x, angle_y, angle_z = result.get()
|
||||
#
|
||||
# # 更新最优旋转角度
|
||||
# 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 = angle_x, angle_y, angle_z
|
||||
#
|
||||
# return best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y
|
||||
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
|
||||
|
||||
#"""
|
||||
@njit
|
||||
def safe_min(arr):
|
||||
@@ -465,9 +414,6 @@ def get_lowest_position_of_center(obj_path,voxel_size,dict_origin,total_matrix):
|
||||
best_angle_x, best_angle_y, best_angle_z = 0, 0, 0
|
||||
start_time = time.time()
|
||||
# 旋转并计算最优角度:绕X、Y、Z轴进行每度的旋转
|
||||
# best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation(points, angle_step=3)
|
||||
# best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation2(points, angle_step=3)
|
||||
# best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation3(points, angle_step=3)
|
||||
best_angle_x, best_angle_y, best_angle_z, min_center_of_mass_y = parallel_rotation4(points, angle_step=3)
|
||||
|
||||
print("get_lowest_position_of_center", obj_path, best_angle_x,best_angle_y,best_angle_z,"time",time.time()-start_time)
|
||||
|
||||
+2
-1
@@ -10,9 +10,10 @@ from config import print_data_dir
|
||||
from config import test_print_max
|
||||
|
||||
from general import mesh_transform_by_matrix
|
||||
from general import read_mesh
|
||||
from general import get_blank_path
|
||||
|
||||
from compute_print_net_out import read_mesh
|
||||
|
||||
def load_and_transform_models(base_path, dict_origin, json_name):
|
||||
|
||||
meshes = [] # 存储所有变换后的网格
|
||||
|
||||
Reference in new issue
Block a user