三维高斯的分裂

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

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)
相关推荐
天天进步201517 小时前
【Nanobrowser源码分析4】交互篇: 从指令到动作:模拟点击、滚动与输入的底层实现
开发语言·javascript·ecmascript
console.log('npc')17 小时前
vue2中子组件父组件的修改参数
开发语言·前端·javascript
码点17 小时前
【无标题】日文字库Japan.ini
开发语言
IT=>小脑虎17 小时前
2026版 Python零基础小白学习知识点【基础版详解】
开发语言·python·学习
我想吃烤肉肉17 小时前
Playwright中page.locator和Selenium中find_element区别
爬虫·python·测试工具·自动化
rabbit_pro17 小时前
Java使用Mybatis-Plus封装动态数据源工具类
java·python·mybatis
wjs202417 小时前
抽象工厂模式
开发语言
lly20240617 小时前
SVG 模糊效果详解
开发语言
期待のcode17 小时前
Java虚拟机类加载机制
java·开发语言
Learner17 小时前
Python运算符
开发语言·python