import numpy as np import open3d as o3d from scipy.spatial.transform import Rotation from scipy.linalg import orthogonal_procrustes from open3d.pipelines.registration import registration_ransac_based_on_correspondence def rigid_transform_3D(A, B): assert A.shape == B.shape, "Input arrays must have the same shape" assert A.shape[1] == 3, "Input arrays must be Nx3" N = A.shape[0] # Number of points # Compute centroids of A and B centroid_A = np.mean(A, axis=0) centroid_B = np.mean(B, axis=0) # Center the points around the centroids AA = A - centroid_A BB = B - centroid_B # H = AA^T * BB H = np.dot(AA.T, BB) # Singular Value Decomposition U, S, Vt = np.linalg.svd(H) # Compute rotation R = np.dot(Vt.T, U.T) # Ensure a proper rotation (det(R) should be +1) if np.linalg.det(R) < 0: Vt[2, :] *= -1 R = np.dot(Vt.T, U.T) # Compute translation t = centroid_B - np.dot(R, centroid_A) # Construct the transform matrix (4x4) transform_matrix = np.eye(4) transform_matrix[:3, :3] = R transform_matrix[:3, 3] = t return transform_matrix def compute_rigid_transform(points1, points2): """ 计算从points1到points2的刚体变换(包括尺度、旋转和平移)。 参数: points1, points2: np.ndarray, 形状为(68, 3)的数组,分别为两组3D对应点。 返回: scale: float, 尺度因子 R: np.ndarray, 3x3的旋转矩阵 t: np.ndarray, 3维的平移向量 """ # 中心化 mean1 = np.mean(points1, axis=0) centered_points1 = points1 - mean1 mean2 = np.mean(points2, axis=0) centered_points2 = points2 - mean2 # 使用orthogonal_procrustes计算旋转和平移 R, _ = orthogonal_procrustes(centered_points1, centered_points2) t = mean2 - R @ mean1 # 计算平移向量 # 计算尺度因子 scale = np.mean(np.linalg.norm(centered_points2, axis=1) / np.linalg.norm(centered_points1, axis=1)) return scale, R, t def compute_rigid_transform_new(points_A, points_B): # 中心化 center_A = np.mean(points_A, axis=0) center_B = np.mean(points_B, axis=0) points_A_centered = points_A - center_A points_B_centered = points_B - center_B # 计算协方差矩阵 cov_matrix = np.dot(points_A_centered.T, points_B_centered) # SVD分解 U, S, Vt = np.linalg.svd(cov_matrix) # 确保旋转矩阵为正交且右手系,这里我们取Vt的转置作为旋转矩阵 rotation_matrix = np.dot(Vt.T, U.T) # 检查行列式是否为-1(表示反射,不满足旋转矩阵要求),如果是,则调整一个列的符号 if np.linalg.det(rotation_matrix) < 0: Vt[2,:] *= -1 rotation_matrix = np.dot(Vt.T, U.T) # 计算尺度因子 scale = np.trace(np.dot(points_A_centered.T, points_B_centered)) / np.trace(np.dot(points_A_centered.T, points_A_centered)) # 计算平移向量 translation_vector = center_B - scale * np.dot(rotation_matrix, center_A) return scale, rotation_matrix, translation_vector # 示范用法 obj_A = '/home/gyalex/Desktop/our_face.obj' obj_B = '/home/gyalex/Desktop/Neutral.obj' mesh_A = o3d.io.read_triangle_mesh(obj_A) mesh_B = o3d.io.read_triangle_mesh(obj_B) vertices_A = np.asarray(mesh_A.vertices) vertices_B = np.asarray(mesh_B.vertices) list_A = list() list_B = list() with open('/home/gyalex/Desktop/our_marker.txt', 'r') as f: lines_A = f.readlines() for line in lines_A: hh = line.strip().split() list_A.append(int(hh[0])) with open('/home/gyalex/Desktop/ARKit_landmarks.txt', 'r') as f: lines_B = f.readlines() for line in lines_B: hh = line.strip().split() list_B.append(int(hh[0])) A = vertices_A[list_A,:] # 第一组3D点 B = vertices_B[list_B,:] # 第二组3D点 # scale, R, t = compute_rigid_transform(A, B) # # 定义尺度变换矩阵 # scale_matrix = np.eye(4) # scale_matrix[0, 0] = scale # x轴方向放大2倍 # scale_matrix[1, 1] = scale # y轴方向放大2倍 # scale_matrix[2, 2] = scale # z轴方向放大2倍 # transform_matrix = np.eye(4) # transform_matrix[:3, :3] = scale # transform_matrix[:3, 3] = R*t # mesh_A.transform(transform_matrix) # # mesh_A.transform(scale_matrix) # o3d.io.write_triangle_mesh('/home/gyalex/Desktop/our_face_new.obj', mesh_A) pcd_source = o3d.utility.Vector3dVector(A) # 示例源点云数据 pcd_target = o3d.utility.Vector3dVector(B) # 示例目标点云数据 + 1偏移,仅作示例 corres_source = list() for idx in range(68): corres_source.append(idx) corres_target = list() for idx in range(68): corres_target.append(idx) # 根据对应点索引获取实际的对应点坐标 corres_source_points = pcd_source corres_target_points = pcd_target corres = o3d.utility.Vector2iVector([[src, tgt] for src, tgt in zip(corres_source, corres_target)]) # 应用RANSAC进行基于对应点的配准 reg_result = registration_ransac_based_on_correspondence( pcd_source, pcd_target, corres, estimation_method=o3d.pipelines.registration.TransformationEstimationPointToPoint(), ransac_n=3, criteria=o3d.pipelines.registration.RANSACConvergenceCriteria(max_iteration=100000, epsilon=1e-6) ) # # 使用RANSAC进行配准 # convergence_criteria = o3d.pipelines.registration.RANSACConvergenceCriteria(max_iteration=50000, max_validation=500) # ransac_result = o3d.pipelines.registration.registration_ransac_based_on_correspondence( # pcd_source, # pcd_target, # corres, # o3d.pipelines.registration.TransformationEstimationPointToPoint(), # 3, # RANSAC阈值,根据实际情况调整 # convergence_criteria, # [o3d.pipelines.registration.CorrespondenceCheckerBasedOnEdgeLength(0.9), # o3d.pipelines.registration.CorrespondenceCheckerBasedOnDistance(0.05)], # o3d.pipelines.registration.RANSACLoss()) # 应用变换到源mesh # mesh_source_aligned = mesh_source.transform(reg_result.transformation) a = 0