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