File size: 6,100 Bytes
17cd746 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
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 |