File size: 3,698 Bytes
23e73f9 | 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 | import os
import json
import numpy as np
from scipy.spatial import cKDTree
from plyfile import PlyData # 仅用于读取 COLMAP 的标准真实点云
def umeyama_transform(P, Q):
"""标准的 Umeyama 算法求解 7-DoF (s, R, t) 变换矩阵"""
mu_P = P.mean(axis=0)
mu_Q = Q.mean(axis=0)
P_centered = P - mu_P
Q_centered = Q - mu_Q
C = P_centered.T @ Q_centered / P.shape[0]
U, S, Vt = np.linalg.svd(C)
R = Vt.T @ U.T
if np.linalg.det(R) < 0:
Vt[2, :] *= -1
R = Vt.T @ U.T
var_P = np.var(P, axis=0).sum()
if var_P < 1e-8:
scale = 1.0
else:
scale = (1.0 / var_P) * np.sum(S)
t = mu_Q - scale * (R @ mu_P)
T = np.eye(4)
T[:3, :3] = scale * R
T[:3, 3] = t
return T
def icp_umeyama_align(source_pts, target_pts, max_iters=20, tolerance=1e-5):
"""使用 ICP 匹配点对,使用 Umeyama 求解矩阵"""
if len(source_pts) > 50000:
idx = np.random.choice(len(source_pts), 50000, replace=False)
src = source_pts[idx].copy()
else:
src = source_pts.copy()
target_tree = cKDTree(target_pts)
T_accum = np.eye(4)
prev_dist = float('inf')
for i in range(max_iters):
dists, indices = target_tree.query(src)
matched_target = target_pts[indices]
T = umeyama_transform(src, matched_target)
src_homo = np.hstack((src, np.ones((src.shape[0], 1))))
src = (T @ src_homo.T).T[:, :3]
T_accum = T @ T_accum
if i > 0 and np.mean(dists) - prev_dist > -tolerance:
break
prev_dist = np.mean(dists)
return T_accum
def assess_manifold_collapse(model, model_dir, colmap_ply_path=None):
try:
# 优雅降级:如果模型没有提供离散空间点(比如隐式表征),直接跳过此项检测
if not hasattr(model, 'get_spatial_centers'):
return {"manifold_collapse": False, "reason": "Model lacks explicit discrete centers"}
try:
xyz_pred = model.get_spatial_centers().cpu().numpy()
except NotImplementedError:
return {"manifold_collapse": False, "reason": "Method not implemented"}
if len(xyz_pred) < 100:
return {"manifold_collapse": True, "reason": "Too few points"}
# --- 拓扑学流形坍塌检测 ---
cov = np.cov(xyz_pred.T)
eigenvalues, _ = np.linalg.eigh(cov)
if eigenvalues[0] < 1e-6:
return {"manifold_collapse": True, "reason": "Zero variance in one dimension (Flat collapse)"}
# --- 锚点绝对坐标系对齐 (ICP-Umeyama) ---
matrix = np.eye(4)
if colmap_ply_path and os.path.exists(colmap_ply_path):
try:
colmap_data = PlyData.read(colmap_ply_path)
cv = colmap_data.elements[0].data
xyz_colmap = np.vstack([cv['x'], cv['y'], cv['z']]).T
print(f" [Alignment] 正在对齐到绝对宇宙 (ICP)... 锚点数量: {len(xyz_colmap)}")
matrix = icp_umeyama_align(xyz_pred, xyz_colmap)
except Exception as e:
print(f" [Alignment] 锚点对齐失败 ({e}),回退为单位矩阵。")
matrix_path = os.path.join(model_dir, "alignment_matrix.json")
with open(matrix_path, 'w') as f:
json.dump(matrix.tolist(), f, indent=4)
return {"manifold_collapse": False, "reason": "Normal"}
except Exception as e:
return {"manifold_collapse": False, "reason": f"Detection skipped due to error: {str(e)}"}
|