将一个三维高斯,分裂成两个小高斯:
python
import numpy as np
# ----------------------------------------------------------------------
# 辅助函数: 四元数-向量乘法 (v' = q * v * q_inv)
# 用于将局部坐标系下的位移向量旋转到世界坐标系
# ----------------------------------------------------------------------
def quaternion_rotate_vector(q, v):
"""
使用四元数 q 旋转向量 v。
Args:
q (np.array): 四元数 (w, x, y, z)。
v (np.array): 3D 向量 (x, y, z)。
Returns:
np.array: 旋转后的 3D 向量。
"""
# 确保输入是 numpy 数组
q = np.array(q)
v = np.array(v)
# 提取四元数分量
w, x, y, z = q
# 构造旋转矩阵 R
# (这是将四元数转换为旋转矩阵并应用到向量的常用方法)
R = np.array([
[1 - 2*y**2 - 2*z**2, 2*x*y - 2*w*z, 2*x*z + 2*w*y],
[2*x*y + 2*w*z, 1 - 2*x**2 - 2*z**2, 2*y*z - 2*w*x],
[2*x*z - 2*w*y, 2*y*z + 2*w*x, 1 - 2*x**2 - 2*y**2]
])
# 应用旋转
v_rotated = np.dot(R, v)
return v_rotated
# ----------------------------------------------------------------------
# 高斯类定义 (Gaussian)
# ----------------------------------------------------------------------
class Gaussian:
"""表示一个 3D Gaussian Splatting 元素及其关键参数。"""
def __init__(self, id, xyz, scale, rotation_wxyz):
self.id = id
self.xyz = np.array(xyz, dtype=np.float32) # 中心坐标 (x, y, z)
self.scale = np.array(scale, dtype=np.float32) # 尺度 (s_w, s_h, s_l)
# 旋转四元数 (w, x, y, z) - 3DGS 常用顺序
self.rotation_wxyz = np.array(rotation_wxyz, dtype=np.float32)
def __str__(self):
return (f"Gaussian ID: {self.id}\n"
f" 中心 (xyz): {self.xyz.round(4)}\n"
f" 尺度 (scale): {self.scale.round(4)}\n"
f" 旋转 (wxyz): {self.rotation_wxyz.round(4)}\n")
# ----------------------------------------------------------------------
# 核心分解逻辑 (Split Gaussian Logic)
# ----------------------------------------------------------------------
def split_gaussian(original_gaussian: Gaussian):
"""
根据 "Clone and Split" 策略,将一个高斯分裂成两个新的高斯。
分裂过程遵循以下步骤:
1. 确定高斯的长轴 (Major Axis),即尺度最大的方向 k。
2. 新高斯的尺度:长轴尺度减半 (s'_k = 0.5 * s_k),其他轴不变。
3. 计算局部位移 (Local Displacement) d_local = 0.5 * s_max * e_k。
4. 将 d_local 旋转到世界坐标系得到世界位移 (World Displacement) Δx = R * d_local。
5. 新中心点:x_1 = x + Δx, x_2 = x - Δx。
6. 旋转和不透明度继承自原高斯。
Args:
original_gaussian (Gaussian): 待分裂的原始高斯对象。
Returns:
tuple: (new_gaussian_1, new_gaussian_2)
"""
print("=" * 50)
print(f"开始分解 高斯 ID: {original_gaussian.id}")
print(f"原始尺度 (w, h, l): {original_gaussian.scale.round(4)}")
s = original_gaussian.scale
# --- Step 1: 确定主轴 (Major Axis) ---
max_scale = np.max(s)
major_axis_index = np.argmax(s)
axis_labels = ['w', 'h', 'l']
major_axis_label = axis_labels[major_axis_index]
print(f"\n[Step 1] 确定主轴:")
print(f" 最大尺度 s_max: {max_scale.round(4)}")
print(f" 主轴索引 k: {major_axis_index} ({major_axis_label} 轴)")
# --- Step 2: 计算新尺度 (New Scales) ---
new_s = s.copy()
new_s[major_axis_index] = max_scale * 0.5
print(f"\n[Step 2] 计算新尺度:")
print(f" 长轴尺度 s'_{major_axis_label} = 0.5 * {max_scale.round(4)} = {new_s[major_axis_index].round(4)}")
print(f" 新尺度 (w', h', l'): {new_s.round(4)}")
# --- Step 3 & 4: 计算位移向量 (Displacement) ---
# 局部位移向量 (沿主轴方向的单位向量)
e_k = np.zeros(3)
e_k[major_axis_index] = 1.0 # 例如,如果主轴是h (索引1),则 e_k = [0, 1, 0]
# 位移幅度 (0.5 * s_max)
displacement_magnitude = max_scale * 0.5
d_local = e_k * displacement_magnitude
# 世界位移 (Δx = R * d_local)
# 使用四元数旋转到世界坐标系
q = original_gaussian.rotation_wxyz
delta_x = quaternion_rotate_vector(q, d_local)
print(f"\n[Step 3/4] 计算世界位移:")
print(f" 局部位移 d_local: {d_local.round(4)}")
print(f" 四元数 q (wxyz): {q.round(4)}")
print(f" 世界位移 Δx: {delta_x.round(4)}")
# --- Step 5: 计算新中心点 (New Centers) ---
# 中心 1: x + Δx
center_1 = original_gaussian.xyz + delta_x
# 中心 2: x - Δx
center_2 = original_gaussian.xyz - delta_x
print(f"\n[Step 5] 计算新中心点:")
print(f" 原中心 x: {original_gaussian.xyz.round(4)}")
print(f" 新中心 x_1: {center_1.round(4)}")
print(f" 新中心 x_2: {center_2.round(4)}")
# --- Step 6: 构造新的高斯对象 (New Gaussians) ---
new_gaussian_1 = Gaussian(
id=f"{original_gaussian.id}_1",
xyz=center_1,
scale=new_s,
rotation_wxyz=original_gaussian.rotation_wxyz # 继承旋转
)
new_gaussian_2 = Gaussian(
id=f"{original_gaussian.id}_2",
xyz=center_2,
scale=new_s,
rotation_wxyz=original_gaussian.rotation_wxyz # 继承旋转
)
print("=" * 50)
return new_gaussian_1, new_gaussian_2
# ----------------------------------------------------------------------
# 演示运行
# ----------------------------------------------------------------------
# 初始参数 (与 Canvas 文件中的 INITIAL_GAUSSIAN_PARAMS 匹配)
# xyz: [0.50, 3.41, 18.38]
# scale: [0.6, 0.38, 0.16] => w=0.6, h=0.38, l=0.16
# rotation (wxyz): [0.87, -0.45, -0.16, 0.05] (Three.js 使用 (x, y, z, w) 顺序)
INITIAL_GAUSSIAN_PARAMS = {
'id': 0,
'xyz': [0.50, 3.41, 18.38],
'scale': [0.6, 0.38, 0.16],
'rotation_wxyz': [0.87, -0.45, -0.16, 0.05]
}
# 1. 创建原始高斯
original = Gaussian(
id=INITIAL_GAUSSIAN_PARAMS['id'],
xyz=INITIAL_GAUSSIAN_PARAMS['xyz'],
scale=INITIAL_GAUSSIAN_PARAMS['scale'],
rotation_wxyz=INITIAL_GAUSSIAN_PARAMS['rotation_wxyz']
)
# 2. 执行分解
g1, g2 = split_gaussian(original)
# 3. 打印结果
print("\n--- 分解结果 (New Gaussians) ---")
print("高斯 1:")
print(g1)
print("高斯 2:")
print(g2)