5 changed files with 52 additions and 782 deletions
@ -1,754 +0,0 @@ |
|||||||
import os.path |
|
||||||
import shutil |
|
||||||
|
|
||||||
import open3d as o3d |
|
||||||
import numpy as np |
|
||||||
import copy |
|
||||||
from tqdm import tqdm |
|
||||||
import time |
|
||||||
import multiprocessing |
|
||||||
import oss2 |
|
||||||
from joblib import Parallel, delayed |
|
||||||
import itertools |
|
||||||
from numba import njit, prange |
|
||||||
#import bpy |
|
||||||
from plyfile import PlyData, PlyElement |
|
||||||
from multiprocessing import Pool, RawArray |
|
||||||
import ctypes |
|
||||||
import itertools |
|
||||||
|
|
||||||
from compute_print_net_out import down_sample |
|
||||||
|
|
||||||
def make_pcd_plane(): |
|
||||||
# 创建Y=0的平面点云 |
|
||||||
width = 60.0 # 平面的宽度 |
|
||||||
height = 60.0 # 平面的高度 |
|
||||||
resolution = 50 # 点云的分辨率,越高越密集 |
|
||||||
|
|
||||||
# 生成平面点云 |
|
||||||
x = np.linspace(-width / 2, width / 2, resolution) |
|
||||||
y = np.linspace(-height / 2, height / 2, resolution) |
|
||||||
xv, yv = np.meshgrid(x, y) |
|
||||||
zv = np.zeros_like(xv) # Y 坐标恒为 0 |
|
||||||
|
|
||||||
# 将网格转换为点云的顶点数组 |
|
||||||
points_plane = np.vstack((xv.flatten(), yv.flatten(), zv.flatten())).T |
|
||||||
|
|
||||||
# 创建Open3D点云对象 |
|
||||||
pcd_plane = o3d.geometry.PointCloud() |
|
||||||
pcd_plane.points = o3d.utility.Vector3dVector(points_plane) |
|
||||||
pcd_plane.paint_uniform_color([0, 1, 0]) |
|
||||||
return pcd_plane |
|
||||||
|
|
||||||
|
|
||||||
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 |
|
||||||
|
|
||||||
#""" |
|
||||||
@njit |
|
||||||
def safe_min(arr): |
|
||||||
if arr.size == 0: |
|
||||||
return np.inf |
|
||||||
return np.min(arr) |
|
||||||
|
|
||||||
# 核心计算函数(支持Numba加速) |
|
||||||
@njit(fastmath=True, cache=True) |
|
||||||
def calculate_rotation_z(angle_x, angle_y, angle_z, points, cos_cache, sin_cache, angle_step): |
|
||||||
#计算单个旋转组合后的重心Z坐标(无显式平移) |
|
||||||
|
|
||||||
if points.shape[0] == 0: |
|
||||||
return np.inf # 返回极大值避免干扰最优解 |
|
||||||
|
|
||||||
# 获取预计算的三角函数值 |
|
||||||
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) |
|
||||||
min_z = safe_min(z_values) |
|
||||||
avg_z = np.mean(z_values) |
|
||||||
return avg_z - min_z # 等效于平移后的重心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 |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
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 = np.asarray(points) |
|
||||||
points_float64 = points_np.astype(np.float64) |
|
||||||
points = np.ascontiguousarray(points_float64) |
|
||||||
|
|
||||||
# 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) |
|
||||||
#""" |
|
||||||
|
|
||||||
#""" |
|
||||||
def rotate_x(angle): |
|
||||||
theta = np.radians(angle) |
|
||||||
return np.array([ |
|
||||||
[1, 0, 0], |
|
||||||
[0, np.cos(theta), -np.sin(theta)], |
|
||||||
[0, np.sin(theta), np.cos(theta)] |
|
||||||
]) |
|
||||||
|
|
||||||
def rotate_y(angle): |
|
||||||
theta = np.radians(angle) |
|
||||||
return np.array([ |
|
||||||
[np.cos(theta), 0, np.sin(theta)], |
|
||||||
[0, 1, 0], |
|
||||||
[-np.sin(theta), 0, np.cos(theta)] |
|
||||||
]) |
|
||||||
|
|
||||||
def rotate_z(angle): |
|
||||||
theta = np.radians(angle) |
|
||||||
return np.array([ |
|
||||||
[np.cos(theta), -np.sin(theta), 0], |
|
||||||
[np.sin(theta), np.cos(theta), 0], |
|
||||||
[0, 0, 1] |
|
||||||
]) |
|
||||||
|
|
||||||
def compute_z_height_and_center(rotated_points): |
|
||||||
z_min, z_max = rotated_points[:, 2].min(), rotated_points[:, 2].max() |
|
||||||
y_min, y_max = rotated_points[:, 1].min(), rotated_points[:, 1].max() |
|
||||||
return (z_max - z_min), (y_min + y_max) / 2 |
|
||||||
|
|
||||||
def init_worker(shared_array, shape, dtype): |
|
||||||
global global_points |
|
||||||
global_points = np.frombuffer(shared_array, dtype=dtype).reshape(shape) |
|
||||||
|
|
||||||
def compute_rotation(args): |
|
||||||
angle_x, angle_y, angle_z = args |
|
||||||
R = rotate_z(angle_z) @ rotate_y(angle_y) @ rotate_x(angle_x) |
|
||||||
rotated_points = global_points @ R.T |
|
||||||
z_height, center_y = compute_z_height_and_center(rotated_points) |
|
||||||
return (angle_x, angle_y, angle_z, z_height, center_y) |
|
||||||
|
|
||||||
def parallel_rotation3(points, angle_step=5): |
|
||||||
# 生成所有旋转角度组合 |
|
||||||
angles = itertools.product( |
|
||||||
np.arange(0, 360, angle_step), |
|
||||||
np.arange(0, 360, angle_step), |
|
||||||
np.arange(0, 360, angle_step) |
|
||||||
) |
|
||||||
|
|
||||||
# 共享内存初始化 |
|
||||||
shape, dtype = points.shape, points.dtype |
|
||||||
shared_array = RawArray(ctypes.c_double, points.size) |
|
||||||
shared_points = np.frombuffer(shared_array, dtype=dtype).reshape(shape) |
|
||||||
np.copyto(shared_points, points) |
|
||||||
|
|
||||||
# 多进程计算 |
|
||||||
with Pool(initializer=init_worker, initargs=(shared_array, shape, dtype)) as pool: |
|
||||||
results = pool.imap_unordered(compute_rotation, angles, chunksize=100) |
|
||||||
|
|
||||||
# 寻找最优解 |
|
||||||
best_angle = (0, 0, 0) |
|
||||||
min_z_height = float('inf') |
|
||||||
min_center_y = 0 |
|
||||||
for result in results: |
|
||||||
if result[3] < min_z_height: |
|
||||||
best_angle = result[:3] |
|
||||||
min_z_height = result[3] |
|
||||||
min_center_y = result[4] |
|
||||||
|
|
||||||
return (*best_angle, min_center_y) |
|
||||||
#""" |
|
||||||
|
|
||||||
#""" |
|
||||||
def parallel_rotation4(points, angle_step=4): |
|
||||||
"""仅绕 Y 轴旋转(假设 X/Z 轴不影响目标函数)""" |
|
||||||
min_center = float('inf') |
|
||||||
best_angle = 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 |
|
||||||
|
|
||||||
""" |
|
||||||
#""" |
|
||||||
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) |
|
||||||
|
|
||||||
#""" |
|
||||||
|
|
||||||
def compute_mesh_center(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 |
|
||||||
|
|
||||||
# 遍历所有顶点累加坐标值 |
|
||||||
for vertex in vertices: |
|
||||||
sum_x += vertex[0] |
|
||||||
sum_y += vertex[1] |
|
||||||
sum_z += vertex[2] |
|
||||||
|
|
||||||
# 计算各坐标轴的平均值 |
|
||||||
centroid = np.array([sum_x / n, sum_y / n, sum_z / n]) |
|
||||||
return centroid |
|
||||||
|
|
||||||
def get_lowest_position_of_center(obj_path,voxel_size,dict_origin,total_matrix): |
|
||||||
|
|
||||||
mesh_obj = read_mesh(obj_path) |
|
||||||
dict_origin[obj_path] = mesh_obj |
|
||||||
# dict_origin[obj_path] = copy.deepcopy(mesh_obj) |
|
||||||
|
|
||||||
#o3d.visualization.draw_geometries([mesh_obj]) |
|
||||||
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: {obj_path}") |
|
||||||
return None |
|
||||||
|
|
||||||
pcd = o3d.geometry.PointCloud() |
|
||||||
pcd.points = o3d.utility.Vector3dVector(vertices) |
|
||||||
# o3d.visualization.draw_geometries([pcd]) |
|
||||||
# pcd = o3d.io.read_point_cloud(ply_file_path) |
|
||||||
|
|
||||||
# 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) |
|
||||||
|
|
||||||
# 确保下采样后有点云 |
|
||||||
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 |
|
||||||
start_time = time.time() |
|
||||||
# 旋转并计算最优角度:绕X、Y、Z轴进行每度的旋转 |
|
||||||
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) |
|
||||||
|
|
||||||
""" |
|
||||||
# if best_angle_x >= 180: |
|
||||||
if best_angle_x >= 155 and best_angle_x <= 325: |
|
||||||
best_angle_x += 180 |
|
||||||
if best_angle_y >= 180: |
|
||||||
best_angle_y += 180 |
|
||||||
if best_angle_z >= 180: |
|
||||||
best_angle_z += 180 |
|
||||||
#""" |
|
||||||
|
|
||||||
# 记录结束时间 |
|
||||||
end_time = time.time() |
|
||||||
elapsed_time = end_time - start_time |
|
||||||
# print(f"Time taken: {elapsed_time:.2f} seconds") |
|
||||||
# 输出最佳的旋转角度 |
|
||||||
# print(f"Best Rotation Angles: angle_x = {best_angle_x}, angle_y = {best_angle_y}, angle_z = {best_angle_z}") |
|
||||||
# print(f"Minimum Y Center of Mass: {min_center_of_mass_y}") |
|
||||||
#time.sleep(1000) |
|
||||||
|
|
||||||
# 使用最佳角度进行旋转并平移obj |
|
||||||
pcd_transformed = copy.deepcopy(mesh_obj) |
|
||||||
#""" |
|
||||||
center = pcd_transformed.get_center() |
|
||||||
#arrow = o3d.geometry.TriangleMesh.create_arrow(0.05, 0.1) |
|
||||||
#arrow.translate(center) |
|
||||||
#o3d.visualization.draw_geometries([pcd_transformed, arrow]) |
|
||||||
|
|
||||||
# 最佳角度旋转 |
|
||||||
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) |
|
||||||
|
|
||||||
# centroid = pcd.get_center() |
|
||||||
centroid = pcd_transformed.get_center() |
|
||||||
# z_mean1 = centroid[2] |
|
||||||
|
|
||||||
T_x = np.eye(4) |
|
||||||
T_x[:3, :3] = R_x |
|
||||||
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
||||||
# center_point = aabb.get_center() |
|
||||||
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 |
|
||||||
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
||||||
# center_point = aabb.get_center() |
|
||||||
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 |
|
||||||
aabb = pcd_transformed.get_axis_aligned_bounding_box() |
|
||||||
# center_point = aabb.get_center() |
|
||||||
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 |
|
||||||
|
|
||||||
#arrow = o3d.geometry.TriangleMesh.create_arrow(0.05, 0.1) |
|
||||||
#arrow.translate(center) |
|
||||||
#o3d.visualization.draw_geometries([pcd_transformed, arrow]) |
|
||||||
#""" |
|
||||||
|
|
||||||
#""" |
|
||||||
#试着旋转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]) |
|
||||||
|
|
||||||
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]) |
|
||||||
|
|
||||||
# centroid = pcd.get_center() |
|
||||||
centroid = pcd_transformed.get_center() |
|
||||||
# z_mean2 = centroid[2] |
|
||||||
|
|
||||||
# |
|
||||||
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]) |
|
||||||
|
|
||||||
# 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) |
|
||||||
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 |
|
||||||
|
|
||||||
translation = total_matrix[:3, 3] |
|
||||||
# print(3,translation) |
|
||||||
|
|
||||||
return pcd_transformed, total_matrix |
|
||||||
|
|
||||||
def axis_angle_to_rotation_matrix(axis, angle): |
|
||||||
"""手动生成旋转矩阵""" |
|
||||||
axis = axis / np.linalg.norm(axis) # 单位化 |
|
||||||
cos_a = np.cos(angle) |
|
||||||
sin_a = np.sin(angle) |
|
||||||
return np.array([ |
|
||||||
[cos_a + axis[0]**2*(1-cos_a), |
|
||||||
axis[0]*axis[1]*(1-cos_a) - axis[2]*sin_a, |
|
||||||
axis[0]*axis[2]*(1-cos_a) + axis[1]*sin_a], |
|
||||||
[axis[1]*axis[0]*(1-cos_a) + axis[2]*sin_a, |
|
||||||
cos_a + axis[1]**2*(1-cos_a), |
|
||||||
axis[1]*axis[2]*(1-cos_a) - axis[0]*sin_a], |
|
||||||
[axis[2]*axis[0]*(1-cos_a) - axis[1]*sin_a, |
|
||||||
axis[2]*axis[1]*(1-cos_a) + axis[0]*sin_a, |
|
||||||
cos_a + axis[2]**2*(1-cos_a)] |
|
||||||
]) |
|
||||||
|
|
||||||
def load_obj_data(get_pid,base_out_dir): |
|
||||||
"""下载obj文件""" |
|
||||||
access_key_id = os.getenv('OSS_TEST_ACCESS_KEY_ID', 'LTAI5tBWbfkZntfJij4Fg9gz') |
|
||||||
access_key_secret = os.getenv('OSS_TEST_ACCESS_KEY_SECRET', 'sYDps1i9NSge6hn130EgpPJezKM6Gx') |
|
||||||
bucket_name = os.getenv('OSS_TEST_BUCKET', 'suwa3d-securedata') |
|
||||||
endpoint = os.getenv('OSS_TEST_ENDPOINT', 'https://oss-cn-shanghai.aliyuncs.com') |
|
||||||
bucket = oss2.Bucket(oss2.Auth(access_key_id, access_key_secret), endpoint, bucket_name) |
|
||||||
# if not os.path.exists(base_out_dir): |
|
||||||
# os.makedirs(base_out_dir) |
|
||||||
prefix = f"objs/print/{get_pid}/" |
|
||||||
out_pid_dir = os.path.join(base_out_dir,str(get_pid)) |
|
||||||
for obj in oss2.ObjectIterator(bucket, prefix=prefix): |
|
||||||
# 如果迭代器至少返回一个对象,那么表示该"文件夹"存在 |
|
||||||
if not os.path.exists(out_pid_dir): |
|
||||||
os.makedirs(out_pid_dir) |
|
||||||
#print("Folder exists.") |
|
||||||
filename = obj.key[len(prefix):] |
|
||||||
if get_pid not in filename: |
|
||||||
continue |
|
||||||
print("获取的文件名称",filename) |
|
||||||
local_path = os.path.join(out_pid_dir, filename) |
|
||||||
# 下载对象到本地文件 |
|
||||||
try: |
|
||||||
bucket.get_object_to_file(obj.key, local_path) |
|
||||||
print(f"Downloaded {obj.key} to {local_path}") |
|
||||||
except: |
|
||||||
print("下载错误",get_pid) |
|
||||||
record_text = "./error_load.txt" |
|
||||||
with open(record_text, 'a') as f: |
|
||||||
f.write(get_pid + '\n') |
|
||||||
|
|
||||||
else: |
|
||||||
# 如果迭代器没有返回任何对象,那么表示该"文件夹"不存在 |
|
||||||
print("ossFolder does not exist.",get_pid) |
|
||||||
|
|
||||||
import re |
|
||||||
def copy_obj_2x(base_obj_dir): |
|
||||||
obj_list = [aa for aa in os.listdir(base_obj_dir) if aa.endswith(".obj")] |
|
||||||
for obj_name in obj_list: |
|
||||||
if "_F" in obj_name: |
|
||||||
continue |
|
||||||
obj_count = obj_name.split("x")[-1].replace(".obj","") |
|
||||||
if not obj_count.isnumeric(): |
|
||||||
match = re.search(r"x(\d+)", obj_name) |
|
||||||
if match: |
|
||||||
obj_count = match.group(1) |
|
||||||
else: |
|
||||||
print("未找到 x 后的数字") |
|
||||||
|
|
||||||
obj_count_num = int(obj_count) |
|
||||||
if obj_count_num!=1: |
|
||||||
for i in range(obj_count_num-1): |
|
||||||
origin_path = os.path.join(base_obj_dir,obj_name) |
|
||||||
dis_path = os.path.join(base_obj_dir,obj_name.replace(".obj",f"_F{i+1}.obj")) |
|
||||||
if not os.path.exists(dis_path): |
|
||||||
shutil.copy(origin_path,dis_path) |
|
||||||
print(dis_path,"复制成功") |
|
||||||
|
|
||||||
def save_mesh_with_blender(obj_transformed, obj_path, apply_transform=True): |
|
||||||
# o3d.io.write_triangle_mesh(obj_path, obj_transformed,write_triangle_uvs=True) |
|
||||||
# return |
|
||||||
"""使用 Blender 导出变换后的网格""" |
|
||||||
# 创建新网格对象 |
|
||||||
mesh = bpy.data.meshes.new("TempMesh") |
|
||||||
mesh.from_pydata( |
|
||||||
np.asarray(obj_transformed.vertices), |
|
||||||
[], |
|
||||||
np.asarray(obj_transformed.triangles).tolist() |
|
||||||
) |
|
||||||
obj = bpy.data.objects.new("TempObject", mesh) |
|
||||||
|
|
||||||
# 链接到场景 |
|
||||||
bpy.context.collection.objects.link(obj) |
|
||||||
|
|
||||||
# 设置上下文 |
|
||||||
original_selection = bpy.context.selected_objects.copy() |
|
||||||
original_active = bpy.context.view_layer.objects.active |
|
||||||
bpy.ops.object.select_all(action='DESELECT') |
|
||||||
obj.select_set(True) |
|
||||||
bpy.context.view_layer.objects.active = obj |
|
||||||
|
|
||||||
# 应用变换 |
|
||||||
if apply_transform: |
|
||||||
bpy.ops.object.transform_apply( |
|
||||||
location=True, |
|
||||||
rotation=True, |
|
||||||
scale=True |
|
||||||
) |
|
||||||
|
|
||||||
# 配置导出参数 |
|
||||||
export_settings = { |
|
||||||
'filepath': obj_path, |
|
||||||
'export_selected_objects': True, |
|
||||||
'export_triangulated_mesh': True, |
|
||||||
'forward_axis': 'Y', |
|
||||||
'up_axis': 'Z', |
|
||||||
'global_scale': 1.0 |
|
||||||
} |
|
||||||
|
|
||||||
# 执行导出 |
|
||||||
try: |
|
||||||
bpy.ops.wm.obj_export(**export_settings) |
|
||||||
finally: |
|
||||||
# 清理临时对象 |
|
||||||
bpy.data.objects.remove(obj, do_unlink=True) |
|
||||||
bpy.data.meshes.remove(mesh) |
|
||||||
|
|
||||||
# 恢复原始上下文 |
|
||||||
bpy.ops.object.select_all(action='DESELECT') |
|
||||||
for o in original_selection: |
|
||||||
o.select_set(True) |
|
||||||
bpy.context.view_layer.objects.active = original_active |
|
||||||
|
|
||||||
if __name__ == '__main__': |
|
||||||
out_dir = "/data/datasets_20t/type_setting_test_data/" |
|
||||||
base_out_dir = f"{out_dir}/8/" |
|
||||||
weight_fix_out_dir = f"{out_dir}/print_weight_fix_data_obj" |
|
||||||
weight_fix_out_ply_dir = f"{out_dir}/print_weight_fix_data_ply" |
|
||||||
copy_obj_2x(base_out_dir) |
|
||||||
Loading…
Reference in new issue