三维高斯的分裂

将一个三维高斯,分裂成两个小高斯:

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)
相关推荐
chao1898442 小时前
MATLAB中的多重网格算法与计算流体动力学
开发语言·算法·matlab
a程序小傲2 小时前
京东Java面试被问:ZGC的染色指针如何实现?内存屏障如何处理?
java·后端·python·面试
精神小伙就是猛2 小时前
C# sealed密封 追本溯源
开发语言·c#
真正的醒悟2 小时前
图解网络35
开发语言·网络·php
大连好光景2 小时前
批量匿名数据重识别(debug记录)
开发语言·python
计算机毕设VX:Fegn08952 小时前
计算机毕业设计|基于Java + vue水果商城系统(源码+数据库+文档)
java·开发语言·数据库·vue.js·spring boot·课程设计
暴风鱼划水2 小时前
算法题(Python)哈希表 | 2.两个数组的交集
python·算法·哈希表
清水白石0082 小时前
《深入 Celery:用 Python 构建高可用任务队列的实战指南》
开发语言·python
Tony Bai3 小时前
Jepsen 报告震动 Go 社区:NATS JetStream 会丢失已确认写入
开发语言·后端·golang