3D Gaussian Splatting文件的压缩【3D高斯泼溅】

在上一篇文章中,我开始研究高斯泼溅(3DGS:3D Gaussian Splatting)。 它的问题之一是数据集并不小。 渲染图看起来不错。

但“自行车”、“卡车”、“花园”数据集分别是一个 1.42GB、0.59GB、1.35GB 的 PLY 文件。 它们几乎按原样加载到 GPU 内存中作为巨大的结构化缓冲区,因此至少也需要那么多的 VRAM,加上更多用于排序,加上在官方查看器实现中,平铺 splat 光栅化使用了数百 MB 。

我可以告诉你,我可以将数据缩小 19 倍(分别为 78、32、74 MB),但看起来并不是那么好。 仍然可以识别,但确实不好 — 但是,这些伪影不是典型的“低 LOD 多边形网格渲染”,它们更像是“空间中的 JPG 伪影”:

在线工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器

然而,在这两个极端之间,还有其他配置,可以使数据小 5 倍到 10 倍,同时看起来还不错。

因此,我们从每个 splat 的 248 字节开始,我们希望将其减少。 注意:在这里我将探索存储和运行时内存的使用,即不是“文件压缩”! 相反,我也想减少 GPU 内存消耗。 减小运行时数据的副作用也会使磁盘上的数据变小,但“存储大小”是另一个完全独立的主题。 也许改天吧!

使用 splat 数据要做的一件明显且简单的事情是注意“法线”(12 字节)完全未使用。 但这并不能节省太多。 那么你当然可以尝试将所有数字设置为 Float16 而不是 Float32,这还算不错,但只会使数据变小 2 倍。

你还可以丢弃所有球谐函数数据,只留下“基色”(即 SH0),这将减少 75% 的数据大小! 这确实会改变照明并消除一些“反射”,并且在运动中更加明显,但逐渐降低质量水平较低的 SH 频段(或逐渐加载它们)是简单且明智的。

当然,让我们看看我们还能做什么:)

1、重新排序并切成块

数据文件中 splats 的顺序并不重要; 无论如何,我们将在渲染时按距离对它们进行排序。 在 PLY 数据文件中,它们实际上是随机的,这里的每个点都是一个图块,颜色是基于点索引的渐变:

但我们可以根据“位置”(或任何其他标准)对它们重新排序。 例如,按 3D Morton 顺序对它们进行排序通常会使空间中的邻近点在数据数组内彼此靠近:

然后,我可以将 splats 分组为 N 块(N = 256 是我的选择),并希望由于它们通常会靠近在一起,也许它们的数据方差较低,或者至少它们的数据可以以某种方式表示 更少的位。 如果我想象块边界框,它们通常很小并且分散在整个场景中:

这几乎就是“从失败中学习”梦想演讲的幻灯片 112-113。

未来的工作:尝试希尔伯特曲线排序而不是莫顿。 还可以尝试“部分填充块”来打破大块边界,每当莫顿曲线翻转到另一侧时就会发生这种情况。

顺便说一句,莫顿重新排序还可以使渲染速度更快,因为即使在按距离排序后,附近的点更有可能位于原始数据数组中附近。 当然,可以在 Fabian 的博客上找到在不依赖 BMI 或类似 CPU 指令的情况下执行 Morton 计算的好代码,此处针对 64 位结果情况进行了调整:

// Based on https://fgiesen.wordpress.com/2009/12/13/decoding-morton-codes/
// Insert two 0 bits after each of the 21 low bits of x
static ulong MortonPart1By2(ulong x)
{
    x &= 0x1fffff;
    x = (x ^ (x << 32)) & 0x1f00000000ffffUL;
    x = (x ^ (x << 16)) & 0x1f0000ff0000ffUL;
    x = (x ^ (x << 8)) & 0x100f00f00f00f00fUL;
    x = (x ^ (x << 4)) & 0x10c30c30c30c30c3UL;
    x = (x ^ (x << 2)) & 0x1249249249249249UL;
    return x;
}
// Encode three 21-bit integers into 3D Morton order
public static ulong MortonEncode3(uint3 v)
{
    return (MortonPart1By2(v.z) << 2) | (MortonPart1By2(v.y) << 1) | MortonPart1By2(v.x);
}

2、使所有数据相对于块为 0..1

现在所有的图块都被切割成 256 个图块大小的块,我们可以计算每个块的所有内容(位置、比例、颜色、SH 等)的最小和最大数据值,并将其存储起来。 我们不关心数据大小(还?); 只需将它们存储在完整的浮动中即可。

现在,调整 splat 数据,使所有数字都在块最小值和最大值之间的 0..1 范围内。 如果像以前一样保留在 Float32 中,那么这并不会以任何明显的方式真正改变精度,只是在渲染着色器内添加一些间接(要计算出最终的 splat 数据,你需要获取块 min 和 max, 并根据 splat 值在它们之间进行插值)。

哦,对于旋转,我以“最小三个”格式对四元数进行编码(存储最小的 3 个分量,加上最大分量的索引)。

现在数据都在 0..1 范围内,我们可以尝试用比完整 Float32 更小的数据类型来表示它!

但首先,所有 0..1 数据是什么样子的? 以下是以 RGB 颜色显示的各种数据,每个图一个像素,按行主要顺序。 通过位置,你可以清楚地看到它在 256 大小的块内发生变化(每条水平线有两个块):

旋转确实有一些水平条纹,但更加随机:

比例也有一些水平模式,但我们也可以看到大多数比例都朝向较小的值:

颜色(SH0)是这样的:

不透明度通常要么几乎透明,要么几乎不透明:

有很多球谐函数带,它们往往看起来像一团乱麻,所以这是其中之一:

3、嘿,这个数据看起来很像纹理!

我们为每个“事物”(位置、颜色、旋转……)提供了 3 或 4 个值,现在这些值都在 0..1 范围内。 我知道! 让我们将它们放入纹理中,每个 splat 一个纹理元素。 然后我们可以轻松地在它们上尝试使用各种纹理格式,并让 GPU 纹理采样硬件完成将数据转换为数字的所有繁重工作。

我不知道,我们甚至可以使用一些疯狂的东西,比如在这些纹理上使用压缩纹理格式(例如 BC1 或 BC7)。 这样效果好吗? 事实证明,不是立即。 这里将所有数据(位置、旋转、比例、颜色/不透明度、SH)转换为 BC7 压缩纹理。 数据只有 122MB(小 12 倍),但与完整 Float32 数据相比,PSNR 低至 21.71:

然而,我们知道 GPU 纹理压缩格式是基于块的,例如 在典型的 PC 上,BCn 压缩格式均基于 4x4 纹素块。 但我们的纹理数据是以 256x1 条带的 splat 块的形式排列的,一个接一个。 让我们对它们进行更多的重新排序,即将每个块布置在 16x16 纹素正方形中,再次按照莫顿顺序排列。

uint EncodeMorton2D_16x16(uint2 c)
{
    uint t = ((c.y & 0xF) << 8) | (c.x & 0xF); // ----EFGH----ABCD
    t = (t ^ (t << 2)) & 0x3333;               // --EF--GH--AB--CD
    t = (t ^ (t << 1)) & 0x5555;               // -E-F-G-H-A-B-C-D
    return (t | (t >> 7)) & 0xFF;              // --------EAFBGCHD
}
uint2 DecodeMorton2D_16x16(uint t)      // --------EAFBGCHD
{
    t = (t & 0xFF) | ((t & 0xFE) << 7); // -EAFBGCHEAFBGCHD
    t &= 0x5555;                        // -E-F-G-H-A-B-C-D
    t = (t ^ (t >> 1)) & 0x3333;        // --EF--GH--AB--CD
    t = (t ^ (t >> 2)) & 0x0f0f;        // ----EFGH----ABCD
    return uint2(t & 0xF, t >> 8);      // --------EFGHABCD
}

如果我们以这种方式重新排列所有纹理数据,那么现在看起来像这样(位置、旋转、缩放、颜色、不透明度、SH1):

将所有这些编码到 BC7 中可以大大提高质量(PSNR 21.71→24.18):

4、那么应该使用什么纹理格式呢?

在尝试了一大堆可能的设置之后,这是我想出的质量设置级别。 格式如下表所示:

  • F32x4:4x Float32(128 位)。 由于 GPU 通常没有三通道 Float32 纹理格式,因此在这种情况下,当只需要三个组件时,我扩展数据毫无用处。
  • F16x4:4x Float16(64 位)。 与上面的 4 个组件类似的扩展。
  • Norm10_2:无符号标准化 10.10.10.2(32 位)。 GPU 确实支持这一点,并且 Unity 几乎支持它 - 它公开了格式枚举成员,但实际上不允许您使用所述格式创建纹理(哈哈!)。 因此,我通过假装纹理采用单个组件 Float32 格式来模拟它,并在着色器中手动“解包”。
  • Norm11:无符号标准化 11.10.11(32 位)。 GPU 没有它,但既然我无论如何都在模拟类似的格式(见上文),那么当我们只需要三个组件时为什么不使用更多的位。
  • Norm8x4:4x 无符号标准化字节(32 位)。
  • Norm565:无符号标准化 5.6.5(16 位)。
  • BC7和BC1:明显,分别是8位和4位。
质量PosRotSclColSHComprPSNR
极高F32x4F32x4F32x4F32x4F32x40.8x
F16x4Norm10_2Norm11F16x4Norm112.9x54.82
Norm11Norm10_2Norm11Norm8x4Norm5655.2x47.82
Norm11Norm10_2Norm565BC7BC112.2x34.79
极低BC7BC7BC7BC7BC118.7x24.02

以下是“参考”图像,1.42GB、0.59GB、1.35GB 数据大小:

“非常低”则主要供参考; 在如此低的质量下它几乎变得毫无用处,74MB、32MB、74MB – 缩小 18.7 倍;PSNR 24.02、22.28、23.1:

5、结论和未来的工作

高斯泼溅数据大小(磁盘上和内存中)可以相当容易地减少 5 倍到 12 倍,渲染质量水平相当可接受。 比如说,对于“花园”场景,1.35GB 数据文件“哎呀,听起来有点过分”,但在 110-260MB 时,它变得更有趣。 绝对还不算小,但更实用。

我认为“以某种方式”排列 splat 数据,然后不仅通过将每个 splat 单独编码为更少量的位,而且还“在邻居内”(例如使用 BC7 或 BC1)来压缩它们,这一想法很有趣。 特别是,即使使用 BC1 压缩,球谐函数数据看起来也相当不错(与“明显错误”的旋转或缩放不同,它有助于判断球谐函数系数何时出错:))。

我可以尝试很多小事情:

  • Splat 重新排序:不仅根据位置重新排序 splat,还根据“其他内容”重新排序。 尝试希尔伯特曲线而不是莫顿曲线。 每当曲线翻转到另一侧时,尝试使用不完全 256 大小的块。
  • 颜色/不透明度编码:也许值得将其放入两个单独的纹理中,而不是尝试让 BC7 压缩它们。
  • 我确实想知道如何降低纹理分辨率,也许对于某些组件(球面谐波?颜色,如果不透明度是独立的?)您可以使用较低分辨率的纹理,即每个splat低于1个纹素。

当然,还有更大的问题,从某种意义上说,这种减少数据大小的方式是否明智。 也许类似于“材质纹理的随机访问神经压缩”(Vaidyanathan、Salvi、Wronski 2023)的东西会起作用? 如果我对“神经/机器学习”这个东西有所了解就好了:)

我的上述所有代码都在 github 上的这个 PR 中。


原文链接:3D高斯泼溅文件压缩 - BimAnt

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/147099.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

西门子精彩触摸屏SMART LINE V4 面板使用U盘下载项目程序的具体方法示例

西门子精彩触摸屏SMART LINE V4 面板使用U盘下载项目程序的具体方法示例 WinCC flexible SMART V4 SP1 软件针对SMART LINE V4 面板新增了使用U盘下载项目功能。 注意:“使用U盘下载项目”功能仅支持触摸屏OS版本为V4.0.1.0 及以上的设备。 使用U盘下载项目的步骤可参考以下内…

php+vue3实现点选验证码

buildadmin 中的点选验证码实现 验证码类 <?phpnamespace ba;use Throwable; use think\facade\Db; use think\facade\Lang; use think\facade\Config;/*** 点选文字验证码类*/ class ClickCaptcha {/*** 验证码过期时间(s)* var int*/private int $expire 600;/*** 可以…

【洛谷算法题】P5711-闰年判断【入门2分支结构】

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【洛谷算法题】 文章目录 【洛谷算法题】P5711-闰年判断【入门2分支结构】&#x1f30f;题目描述&#x1f30f;输入格式&a…

【LeetCode刷题笔记】二叉树(二)

257. 二叉树的所有路径 解题思路: 1. DFS 前序遍历 ,每次递归将 当前节点的拼接结果 传递到 下一层 中,如果当前节点是 叶子节点 ,就将 当前拼接结果 收集答案并返回。 注意:路径path结果可以使用 String 来拼接,这样可以避免回溯处理。

Scala---方法与函数

一、Scala方法的定义 有参方法&无参方法 def fun (a: Int , b: Int) : Unit {println(ab) } fun(1,1)def fun1 (a: Int , b: Int) ab println(fun1(1,2)) 注意点&#xff1a; 方法定义语法 用def来定义可以定义传入的参数&#xff0c;要指定传入参数的类型方法可以写返…

时间序列基础->数据标签、数据分割器、数据加载器的定义和讲解(零基础入门时间序列)

一、本文介绍 各位小伙伴好&#xff0c;最近在发时间序列的实战案例中总是有一些朋友问我时间序列中的部分对数据的操作是什么含义&#xff0c;我进行了挺多的介绍和讲解但是问的人越来越多&#xff0c;所以今天在这里单独发一篇文章来单独的讲一下时间序列中对数据的处理操作…

PHP使用文件缓存实现html静态化

<?php // 动态生成的内容 $content "<html><body><h1>time:".date("Y-m-d H:i:s")."</h1></body></html>"; // 静态文件保存路径和文件名 $staticFilePath "file.html"; if(file_exists($s…

【汇编】内存的读写与地址空间、寄存器及数据存储

文章目录 前言一、CPU对存储器的读写1.1 cpu对存储器的读写如何进行&#xff1f;1.2 演示 二、内存地址空间三、将各类存储器看作一个逻辑存储器——统一编址内存地址空间的分配方案 三、CPU的组成寄存器是CPU内部的信息存储单元通用寄存器--AX为例“横看成岭侧成峰“ 四、“字…

工具及方法 - 手机扫条码工具: SCANDIT APP

一般扫个链接使用微信扫一扫即可。扫具体条码&#xff0c;可以在微信里搜索小程序&#xff0c;打开也能扫&#xff0c;得到条码内容。 还有其他方式&#xff0c;比如使用淘宝、百度等APP也可以直接扫码条码&#xff0c;还能得到更多的信息。 使用百度的话&#xff0c;不扫条码…

Vue中methods实现原理

目录 前言 回调函数中的this指向问题 vue实例访问methods methods实现原理 前言 vue实例对象为什么可以访问methods中的函数方法&#xff1f;methods的实现原理是什么&#xff1f; 回调函数中的this指向问题 在解答前言中的问题前&#xff0c;需要了解一下回调函数中的th…

振南技术干货集:深入浅出的Bootloader(2)

注解目录 1、烧录方式的更新迭代 1.1 古老的烧录方式 (怀旧一下&#xff0c;单片机高压烧录器。) 1.2 ISP 与ICP 烧录方式 (还记得当年我们玩过的 AT89S51?) 1.3 更方便的 ISP 烧录方式 1.3.1串口 ISP &#xff08;是 STC 单片机成就了我们&#xff0c;还是我们成就了…

C#,数值计算——函数计算,Ratfn的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { public class Ratfn { private double[] cofs { get; set; } private int nn { get; set; } private int dd { get; set; } public Ratfn(double[] num, double[] den) { …

编辑器vim和编译器gcc/g++

目录 一、编辑器vim 1、概念 2、基本操作 1、进入vim 2、模式切换 3、命令行模式 4、插入模式 5、底行模式 6、vim 的配置 二、编译器gcc/g 1、概念 2、背景知识 3、gcc/g中的编译链接 1、预处理 2、编译 3、汇编 4、链接 4、函数库 1、静态库 2、动态库 一…

服务器数据恢复—服务器发生故障导致数据丢失如何恢复服务器数据?

服务器常见故障&#xff1a; 硬件故障&#xff1a;磁盘、板卡、电源故障等。 软件故障&#xff1a;操作系统崩溃、程序运行错误等。 入侵破坏&#xff1a;加密、删除服务数据等。 不可控力&#xff1a;浸水、火烧、倒塌等。 误操作&#xff1a;格式化、删除、覆盖等。 如何减少…

详解[ZJCTF 2019]NiZhuanSiWei 1(PHP两种伪协议、PHP反序列化漏洞、PHP强比较)还有那道题有这么经典?

题目环境&#xff1a; <?php $text $_GET["text"]; $file $_GET["file"]; $password $_GET["password"]; if(isset($text)&&(file_get_contents($text,r)"welcome to the zjctf")){echo "<br><h1>&…

WebStorm配置less编译wxss或css

文章目录 前言先下载安装less程序&#xff1a;实参&#xff1a;要刷新的输出路径成功 前言 使用WebStorm写微信小程序&#xff0c;wxss写着很麻烦&#xff0c;就想着用less&#xff0c;接下来是配置less编译 先下载安装less npm install -g lessless会安装在你当前目录下(以D…

虹科示波器 | 汽车免拆检修 | 2014款保时捷卡宴车行驶中发动机偶尔自动熄火

一、故障现象 一辆2014款保时捷卡宴车&#xff0c;搭载4.8L自然吸气发动机&#xff0c;累计行驶里程约为10.3万km。车主反映&#xff0c;行驶中发动机偶尔自动熄火&#xff0c;尤其在减速至停车的过程中故障容易出现。 二、故障诊断 接车后路试&#xff0c;确认故障现象与车主所…

caspp attacker lab

attacker lab phase2 advice phase 1 ctarget 会先调用test , test调用getbuf, getbuf调用Get。 任务目的是通过缓冲区注入攻击&#xff0c;将函数getbuf返回直接重定向到函数touch1。 0x28 是 40 比特&#xff0c; gdb ./ctarget getbuf 下一次执行的指令是401976, rsp对…

Vue快速入门

目录 一、概述 环境准备 前置知识 JavaScript-导入导出 默认导入导出 二、局部使用Vue 1、使用步骤 准备工作 构建用户界面 2、常用指令 v-for v-bind v-if & v-show v-on v-model 3、生命周期 三、Axios 使用案例1 测试 使用案例2 测试 请求方式别名…

ARMday06(总线、串口、RCC章节分析)

总线 总线是完成各个部件之间传输的一种媒介 串行/并行总线 串行总线&#xff0c; 在同一时刻&#xff0c;根据时钟线的变化&#xff0c;只可以收发一位数据 优点&#xff1a;占用引脚资源少 缺点&#xff1a;传输速度比较慢 并行总线&#xff0c; 在同一时刻&#xff…