gsvvcompressor 1.2.0__cp310-cp310-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gsvvcompressor/__init__.py +13 -0
- gsvvcompressor/__main__.py +243 -0
- gsvvcompressor/combinations/__init__.py +84 -0
- gsvvcompressor/combinations/registry.py +52 -0
- gsvvcompressor/combinations/vq_xyz_1mask.py +89 -0
- gsvvcompressor/combinations/vq_xyz_1mask_zstd.py +103 -0
- gsvvcompressor/combinations/vq_xyz_draco.py +468 -0
- gsvvcompressor/combinations/vq_xyz_draco_2pass.py +156 -0
- gsvvcompressor/combinations/vq_xyz_zstd.py +106 -0
- gsvvcompressor/compress/__init__.py +5 -0
- gsvvcompressor/compress/zstd.py +144 -0
- gsvvcompressor/decoder.py +155 -0
- gsvvcompressor/deserializer.py +42 -0
- gsvvcompressor/draco/__init__.py +34 -0
- gsvvcompressor/draco/draco_decoder.exe +0 -0
- gsvvcompressor/draco/draco_encoder.exe +0 -0
- gsvvcompressor/draco/dracoreduced3dgs.cp310-win_amd64.pyd +0 -0
- gsvvcompressor/draco/interface.py +339 -0
- gsvvcompressor/draco/serialize.py +235 -0
- gsvvcompressor/draco/twopass.py +359 -0
- gsvvcompressor/encoder.py +122 -0
- gsvvcompressor/interframe/__init__.py +11 -0
- gsvvcompressor/interframe/combine.py +271 -0
- gsvvcompressor/interframe/decoder.py +99 -0
- gsvvcompressor/interframe/encoder.py +92 -0
- gsvvcompressor/interframe/interface.py +221 -0
- gsvvcompressor/interframe/twopass.py +226 -0
- gsvvcompressor/io/__init__.py +31 -0
- gsvvcompressor/io/bytes.py +103 -0
- gsvvcompressor/io/config.py +78 -0
- gsvvcompressor/io/gaussian_model.py +127 -0
- gsvvcompressor/movecameras.py +33 -0
- gsvvcompressor/payload.py +34 -0
- gsvvcompressor/serializer.py +42 -0
- gsvvcompressor/vq/__init__.py +15 -0
- gsvvcompressor/vq/interface.py +324 -0
- gsvvcompressor/vq/singlemask.py +127 -0
- gsvvcompressor/vq/twopass.py +1 -0
- gsvvcompressor/xyz/__init__.py +26 -0
- gsvvcompressor/xyz/dense.py +39 -0
- gsvvcompressor/xyz/interface.py +382 -0
- gsvvcompressor/xyz/knn.py +141 -0
- gsvvcompressor/xyz/quant.py +143 -0
- gsvvcompressor/xyz/size.py +44 -0
- gsvvcompressor/xyz/twopass.py +1 -0
- gsvvcompressor-1.2.0.dist-info/METADATA +690 -0
- gsvvcompressor-1.2.0.dist-info/RECORD +50 -0
- gsvvcompressor-1.2.0.dist-info/WHEEL +5 -0
- gsvvcompressor-1.2.0.dist-info/licenses/LICENSE +21 -0
- gsvvcompressor-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,690 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gsvvcompressor
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: A compression library for Gaussian Splatting video/volumetric data with vector quantization and inter-frame encoding
|
|
5
|
+
Author-email: Howard Yin <yindaheng98@gmail.com>
|
|
6
|
+
Maintainer-email: Howard Yin <yindaheng98@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/yindaheng98/gsvvcompressor
|
|
9
|
+
Project-URL: Documentation, https://github.com/yindaheng98/gsvvcompressor#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/yindaheng98/gsvvcompressor
|
|
11
|
+
Project-URL: Issues, https://github.com/yindaheng98/gsvvcompressor/issues
|
|
12
|
+
Keywords: gaussian-splatting,3dgs,compression,video-compression,volumetric-video,vector-quantization,zstd,pytorch,deep-learning
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
24
|
+
Classifier: Topic :: Multimedia :: Video
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Dist: torch>=2.0.0
|
|
30
|
+
Requires-Dist: numpy>=1.21.0
|
|
31
|
+
Requires-Dist: cloudpickle>=2.0.0
|
|
32
|
+
Requires-Dist: zstandard>=0.18.0
|
|
33
|
+
Requires-Dist: hydra-core>=1.3.0
|
|
34
|
+
Requires-Dist: omegaconf>=2.3.0
|
|
35
|
+
Requires-Dist: gaussian-splatting
|
|
36
|
+
Requires-Dist: reduced-3dgs
|
|
37
|
+
Requires-Dist: gscompressor
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# gsvvcompressor
|
|
41
|
+
|
|
42
|
+
A modular video compression framework for Gaussian Splatting models (3DGS). This library provides a flexible, extensible architecture for encoding and decoding sequences of `GaussianModel` frames using inter-frame compression techniques.
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
Install from GitHub:
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
pip install git+https://github.com/yindaheng98/gsvvcompressor.git@master
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or clone and install locally:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
git clone https://github.com/yindaheng98/gsvvcompressor.git
|
|
56
|
+
cd gsvvcompressor
|
|
57
|
+
pip install -e .
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Requirements
|
|
61
|
+
|
|
62
|
+
- Python >= 3.9
|
|
63
|
+
- PyTorch >= 2.0.0
|
|
64
|
+
- Dependencies are automatically installed (numpy, cloudpickle, zstandard, hydra-core, etc.)
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Command-Line Interface
|
|
69
|
+
|
|
70
|
+
Encode a sequence of GaussianModel frames:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
python -m gsvvcompressor encode vqxyzzstd \
|
|
74
|
+
input.first_frame_path=data/frame_0000.ply \
|
|
75
|
+
input.subsequent_format="data/frame_{:04d}.ply" \
|
|
76
|
+
output.path=compressed.bin
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Decode compressed data back to frames:
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
python -m gsvvcompressor decode vqxyzzstd \
|
|
83
|
+
input.path=compressed.bin \
|
|
84
|
+
output.first_frame_path=decoded/frame_0000.ply \
|
|
85
|
+
output.subsequent_format="decoded/frame_{:04d}.ply"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Programmatic Usage
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from gsvvcompressor.combinations import VQXYZZstdEncoder, VQXYZZstdDecoder
|
|
92
|
+
from gsvvcompressor.vq import VQInterframeCodecConfig
|
|
93
|
+
from gsvvcompressor.xyz import XYZQuantInterframeCodecConfig
|
|
94
|
+
from gsvvcompressor.io import FrameReader, FrameWriter, BytesReader, BytesWriter
|
|
95
|
+
|
|
96
|
+
# Create encoder
|
|
97
|
+
encoder = VQXYZZstdEncoder(
|
|
98
|
+
vq_config=VQInterframeCodecConfig(num_clusters=256),
|
|
99
|
+
xyz_config=XYZQuantInterframeCodecConfig(alpha=0.2),
|
|
100
|
+
zstd_level=7,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
# Read frames and encode
|
|
104
|
+
frame_reader = FrameReader(
|
|
105
|
+
first_frame_path="data/frame_0000.ply",
|
|
106
|
+
subsequent_format="data/frame_{:04d}.ply",
|
|
107
|
+
start_index=1,
|
|
108
|
+
)
|
|
109
|
+
encoded_stream = encoder.encode_stream(frame_reader.read())
|
|
110
|
+
|
|
111
|
+
# Write compressed output
|
|
112
|
+
bytes_writer = BytesWriter("compressed.bin")
|
|
113
|
+
bytes_writer.write(encoded_stream)
|
|
114
|
+
|
|
115
|
+
# Decode
|
|
116
|
+
decoder = VQXYZZstdDecoder()
|
|
117
|
+
bytes_reader = BytesReader("compressed.bin")
|
|
118
|
+
decoded_stream = decoder.decode_stream(bytes_reader.read())
|
|
119
|
+
|
|
120
|
+
# Write decoded frames
|
|
121
|
+
frame_writer = FrameWriter(
|
|
122
|
+
first_frame_path="decoded/frame_0000.ply",
|
|
123
|
+
subsequent_format="decoded/frame_{:04d}.ply",
|
|
124
|
+
start_index=1,
|
|
125
|
+
)
|
|
126
|
+
frame_writer.write(decoded_stream)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Architecture Overview
|
|
130
|
+
|
|
131
|
+
The library uses a layered, modular architecture that separates concerns into distinct components:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
135
|
+
│ GaussianModel Stream │
|
|
136
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
137
|
+
│
|
|
138
|
+
▼
|
|
139
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
140
|
+
│ AbstractEncoder / AbstractDecoder │
|
|
141
|
+
│ ┌───────────────────────────────────────────────────────────────┐ │
|
|
142
|
+
│ │ InterframeEncoder / InterframeDecoder │ │
|
|
143
|
+
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
|
|
144
|
+
│ │ │ CombinedInterframeCodecInterface │ │ │
|
|
145
|
+
│ │ │ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │
|
|
146
|
+
│ │ │ │ XYZ Codec │ │ VQ Codec │ │ │ │
|
|
147
|
+
│ │ │ │ (coordinates) │ │ (attributes) │ │ │ │
|
|
148
|
+
│ │ │ └─────────────────┘ └─────────────────────────────┘ │ │ │
|
|
149
|
+
│ │ └─────────────────────────────────────────────────────────┘ │ │
|
|
150
|
+
│ └───────────────────────────────────────────────────────────────┘ │
|
|
151
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
152
|
+
│
|
|
153
|
+
▼
|
|
154
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
155
|
+
│ Payload │
|
|
156
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
157
|
+
│
|
|
158
|
+
▼
|
|
159
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
160
|
+
│ AbstractSerializer / AbstractDeserializer │
|
|
161
|
+
│ ┌───────────────────────────────────────────────────────────────┐ │
|
|
162
|
+
│ │ ZstdSerializer / ZstdDeserializer │ │
|
|
163
|
+
│ │ (cloudpickle + length-prefix framing + zstd compression) │ │
|
|
164
|
+
│ └───────────────────────────────────────────────────────────────┘ │
|
|
165
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
166
|
+
│
|
|
167
|
+
▼
|
|
168
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
169
|
+
│ Bytes Stream │
|
|
170
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Core Abstract Classes
|
|
174
|
+
|
|
175
|
+
### Payload
|
|
176
|
+
|
|
177
|
+
`Payload` is the abstract base class for intermediate data structures that flow between the encoding/decoding stages and the serialization/deserialization stages.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
from gsvvcompressor import Payload
|
|
181
|
+
|
|
182
|
+
@dataclass
|
|
183
|
+
class MyPayload(Payload):
|
|
184
|
+
data: torch.Tensor
|
|
185
|
+
|
|
186
|
+
def to(self, device) -> Self:
|
|
187
|
+
return MyPayload(data=self.data.to(device))
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### AbstractEncoder and AbstractDecoder
|
|
191
|
+
|
|
192
|
+
These are the top-level abstract classes that define the encoding/decoding interface. They use a two-stage process:
|
|
193
|
+
|
|
194
|
+
**Encoding Pipeline:**
|
|
195
|
+
1. `pack(frame)` → Converts `GaussianModel` to `Payload`
|
|
196
|
+
2. `serialize_frame(payload)` → Converts `Payload` to `bytes`
|
|
197
|
+
|
|
198
|
+
**Decoding Pipeline:**
|
|
199
|
+
1. `deserialize_frame(data)` → Converts `bytes` to `Payload`
|
|
200
|
+
2. `unpack(payload)` → Converts `Payload` to `GaussianModel`
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
from gsvvcompressor import AbstractEncoder, AbstractDecoder
|
|
204
|
+
|
|
205
|
+
class MyEncoder(AbstractEncoder):
|
|
206
|
+
def pack(self, frame: GaussianModel) -> Iterator[Payload]:
|
|
207
|
+
# Convert frame to payload(s)
|
|
208
|
+
yield MyPayload(...)
|
|
209
|
+
|
|
210
|
+
def flush_pack(self) -> Iterator[Payload]:
|
|
211
|
+
# Flush any buffered payloads
|
|
212
|
+
return
|
|
213
|
+
yield
|
|
214
|
+
|
|
215
|
+
class MyDecoder(AbstractDecoder):
|
|
216
|
+
def unpack(self, payload: Payload) -> Iterator[GaussianModel]:
|
|
217
|
+
# Convert payload to frame(s)
|
|
218
|
+
yield reconstructed_frame
|
|
219
|
+
|
|
220
|
+
def flush_unpack(self) -> Iterator[GaussianModel]:
|
|
221
|
+
# Flush any buffered frames
|
|
222
|
+
return
|
|
223
|
+
yield
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### AbstractSerializer and AbstractDeserializer
|
|
227
|
+
|
|
228
|
+
These classes handle the conversion between `Payload` objects and `bytes`.
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from gsvvcompressor import AbstractSerializer, AbstractDeserializer
|
|
232
|
+
|
|
233
|
+
class MySerializer(AbstractSerializer):
|
|
234
|
+
def serialize_frame(self, payload: Payload) -> Iterator[bytes]:
|
|
235
|
+
yield serialized_bytes
|
|
236
|
+
|
|
237
|
+
def flush(self) -> Iterator[bytes]:
|
|
238
|
+
yield remaining_bytes
|
|
239
|
+
|
|
240
|
+
class MyDeserializer(AbstractDeserializer):
|
|
241
|
+
def deserialize_frame(self, data: bytes) -> Iterator[Payload]:
|
|
242
|
+
yield deserialized_payload
|
|
243
|
+
|
|
244
|
+
def flush(self) -> Iterator[Payload]:
|
|
245
|
+
yield remaining_payloads
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Inter-frame Compression
|
|
249
|
+
|
|
250
|
+
The `interframe` module provides infrastructure for inter-frame compression, where frames are encoded as differences from previous frames.
|
|
251
|
+
|
|
252
|
+
### InterframeCodecInterface
|
|
253
|
+
|
|
254
|
+
This is the core abstract interface for implementing inter-frame codecs. It defines methods for:
|
|
255
|
+
|
|
256
|
+
- **Keyframe encoding/decoding**: First frame contains full data
|
|
257
|
+
- **Interframe encoding/decoding**: Subsequent frames contain only differences
|
|
258
|
+
- **Context management**: Converting between frames and codec-specific contexts
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from gsvvcompressor.interframe import (
|
|
262
|
+
InterframeCodecInterface,
|
|
263
|
+
InterframeCodecContext,
|
|
264
|
+
InterframeEncoderInitConfig,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
class MyCodecInterface(InterframeCodecInterface):
|
|
268
|
+
def keyframe_to_context(self, frame: GaussianModel, init_config) -> MyContext:
|
|
269
|
+
"""Convert a keyframe to codec context (encoder only)."""
|
|
270
|
+
...
|
|
271
|
+
|
|
272
|
+
def interframe_to_context(self, frame: GaussianModel, prev_context) -> MyContext:
|
|
273
|
+
"""Convert a frame using previous context as reference (encoder only)."""
|
|
274
|
+
...
|
|
275
|
+
|
|
276
|
+
def encode_keyframe(self, context: MyContext) -> MyKeyframePayload:
|
|
277
|
+
"""Encode context as keyframe payload."""
|
|
278
|
+
...
|
|
279
|
+
|
|
280
|
+
def encode_interframe(self, prev_context, next_context) -> MyInterframePayload:
|
|
281
|
+
"""Encode difference between two contexts."""
|
|
282
|
+
...
|
|
283
|
+
|
|
284
|
+
def decode_keyframe(self, payload: MyKeyframePayload) -> MyContext:
|
|
285
|
+
"""Decode keyframe payload to context."""
|
|
286
|
+
...
|
|
287
|
+
|
|
288
|
+
def decode_interframe(self, payload, prev_context) -> MyContext:
|
|
289
|
+
"""Decode interframe payload using previous context."""
|
|
290
|
+
...
|
|
291
|
+
|
|
292
|
+
def context_to_frame(self, context: MyContext, frame: GaussianModel) -> GaussianModel:
|
|
293
|
+
"""Convert context back to frame (decoder only)."""
|
|
294
|
+
...
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### InterframeEncoder and InterframeDecoder
|
|
298
|
+
|
|
299
|
+
These classes implement `AbstractEncoder` and `AbstractDecoder` using an `InterframeCodecInterface`:
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
from gsvvcompressor.interframe import InterframeEncoder, InterframeDecoder
|
|
303
|
+
|
|
304
|
+
# Create encoder with a codec interface
|
|
305
|
+
encoder = InterframeEncoder(
|
|
306
|
+
serializer=my_serializer,
|
|
307
|
+
interface=my_codec_interface,
|
|
308
|
+
init_config=my_init_config,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# Create decoder
|
|
312
|
+
decoder = InterframeDecoder(
|
|
313
|
+
deserializer=my_deserializer,
|
|
314
|
+
interface=my_codec_interface,
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### CombinedInterframeCodecInterface
|
|
319
|
+
|
|
320
|
+
This class allows combining multiple `InterframeCodecInterface` instances into a single codec. Each sub-codec processes different aspects of the frame data:
|
|
321
|
+
|
|
322
|
+
```python
|
|
323
|
+
from gsvvcompressor.interframe.combine import CombinedInterframeCodecInterface
|
|
324
|
+
|
|
325
|
+
# Combine XYZ (coordinates) and VQ (attributes) codecs
|
|
326
|
+
combined = CombinedInterframeCodecInterface([
|
|
327
|
+
xyz_interface, # Handles xyz coordinates
|
|
328
|
+
vq_interface, # Handles other attributes (rotation, scaling, etc.)
|
|
329
|
+
])
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Built-in Codec Implementations
|
|
333
|
+
|
|
334
|
+
### VQ (Vector Quantization) Codec
|
|
335
|
+
|
|
336
|
+
The VQ codec uses vector quantization to compress Gaussian model attributes (rotation, opacity, scaling, features). It maintains a codebook generated from the keyframe.
|
|
337
|
+
|
|
338
|
+
```python
|
|
339
|
+
from gsvvcompressor.vq import VQInterframeCodecInterface, VQInterframeCodecConfig
|
|
340
|
+
|
|
341
|
+
vq_config = VQInterframeCodecConfig(
|
|
342
|
+
num_clusters=256, # Default clusters for each attribute
|
|
343
|
+
num_clusters_rotation_re=256, # Clusters for rotation real part
|
|
344
|
+
num_clusters_rotation_im=256, # Clusters for rotation imaginary part
|
|
345
|
+
num_clusters_opacity=256, # Clusters for opacity
|
|
346
|
+
num_clusters_scaling=256, # Clusters for scaling
|
|
347
|
+
num_clusters_features_dc=256, # Clusters for DC features
|
|
348
|
+
max_sh_degree=3, # Maximum SH degree
|
|
349
|
+
tol=1e-6, # K-means tolerance
|
|
350
|
+
max_iter=500, # K-means max iterations
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
vq_interface = VQInterframeCodecInterface()
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**How it works:**
|
|
357
|
+
- **Keyframe**: Runs K-means clustering to generate codebooks, stores codebooks + cluster IDs
|
|
358
|
+
- **Interframe**: Uses existing codebooks to find nearest cluster IDs, stores only changed IDs with masks
|
|
359
|
+
|
|
360
|
+
### XYZ Quantization Codec
|
|
361
|
+
|
|
362
|
+
The XYZ codec quantizes the 3D coordinates of Gaussian splats using uniform quantization with adaptive step sizes.
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
from gsvvcompressor.xyz import XYZQuantInterframeCodecInterface, XYZQuantInterframeCodecConfig
|
|
366
|
+
|
|
367
|
+
xyz_config = XYZQuantInterframeCodecConfig(
|
|
368
|
+
k=1, # K-th nearest neighbor for step size estimation
|
|
369
|
+
sample_size=10000, # Points to sample for NN estimation
|
|
370
|
+
seed=42, # Random seed
|
|
371
|
+
quantile=0.05, # Quantile of NN distances for scale estimation
|
|
372
|
+
alpha=0.2, # Scaling factor for step size
|
|
373
|
+
min_step=None, # Optional minimum step size
|
|
374
|
+
max_step=None, # Optional maximum step size
|
|
375
|
+
tolerance=0, # Tolerance for change detection
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
xyz_interface = XYZQuantInterframeCodecInterface()
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**How it works:**
|
|
382
|
+
- **Keyframe**: Computes quantization config (step_size, origin) from point distribution, stores config + quantized coordinates
|
|
383
|
+
- **Interframe**: Uses existing config to quantize coordinates, stores only changed coordinates with masks
|
|
384
|
+
|
|
385
|
+
### Zstd Serializer
|
|
386
|
+
|
|
387
|
+
The Zstd serializer uses cloudpickle for serialization with zstandard compression:
|
|
388
|
+
|
|
389
|
+
```python
|
|
390
|
+
from gsvvcompressor.compress.zstd import ZstdSerializer, ZstdDeserializer
|
|
391
|
+
|
|
392
|
+
serializer = ZstdSerializer(level=7) # Compression level 1-22
|
|
393
|
+
deserializer = ZstdDeserializer()
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Pre-built Combinations
|
|
397
|
+
|
|
398
|
+
### VQXYZZstd
|
|
399
|
+
|
|
400
|
+
A ready-to-use encoder/decoder combining VQ + XYZ quantization + Zstd compression:
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
from gsvvcompressor.combinations import (
|
|
404
|
+
VQXYZZstdEncoder,
|
|
405
|
+
VQXYZZstdDecoder,
|
|
406
|
+
VQXYZZstdEncoderConfig,
|
|
407
|
+
VQXYZZstdDecoderConfig,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Direct construction
|
|
411
|
+
encoder = VQXYZZstdEncoder(
|
|
412
|
+
vq_config=VQInterframeCodecConfig(...),
|
|
413
|
+
xyz_config=XYZQuantInterframeCodecConfig(...),
|
|
414
|
+
zstd_level=7,
|
|
415
|
+
payload_device="cpu",
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
decoder = VQXYZZstdDecoder(payload_device="cpu")
|
|
419
|
+
|
|
420
|
+
# Or from config
|
|
421
|
+
from gsvvcompressor.combinations import build_vqxyzzstd_encoder, build_vqxyzzstd_decoder
|
|
422
|
+
|
|
423
|
+
encoder_config = VQXYZZstdEncoderConfig(
|
|
424
|
+
vq=VQInterframeCodecConfig(...),
|
|
425
|
+
xyz=XYZQuantInterframeCodecConfig(...),
|
|
426
|
+
zstd_level=7,
|
|
427
|
+
)
|
|
428
|
+
encoder = build_vqxyzzstd_encoder(encoder_config)
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
## Registry System
|
|
432
|
+
|
|
433
|
+
The `combinations` module provides a registry for encoder/decoder combinations:
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
from gsvvcompressor.combinations import (
|
|
437
|
+
ENCODERS,
|
|
438
|
+
DECODERS,
|
|
439
|
+
register_encoder,
|
|
440
|
+
register_decoder,
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
# List available codecs
|
|
444
|
+
print(ENCODERS.keys()) # ['vqxyzzstd', ...]
|
|
445
|
+
print(DECODERS.keys()) # ['vqxyzzstd', ...]
|
|
446
|
+
|
|
447
|
+
# Register a custom codec
|
|
448
|
+
register_encoder(
|
|
449
|
+
name="mycodec",
|
|
450
|
+
factory=build_my_encoder,
|
|
451
|
+
config_class=MyEncoderConfig,
|
|
452
|
+
description="My custom encoder",
|
|
453
|
+
)
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## IO Module
|
|
457
|
+
|
|
458
|
+
The `io` module provides utilities for reading and writing frames and bytes:
|
|
459
|
+
|
|
460
|
+
```python
|
|
461
|
+
from gsvvcompressor.io import (
|
|
462
|
+
FrameReader,
|
|
463
|
+
FrameWriter,
|
|
464
|
+
BytesReader,
|
|
465
|
+
BytesWriter,
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
# Read GaussianModel frames
|
|
469
|
+
reader = FrameReader(
|
|
470
|
+
first_frame_path="data/frame_0000.ply",
|
|
471
|
+
subsequent_format="data/frame_{:04d}.ply",
|
|
472
|
+
start_index=1,
|
|
473
|
+
sh_degree=3,
|
|
474
|
+
)
|
|
475
|
+
for frame in reader.read():
|
|
476
|
+
process(frame)
|
|
477
|
+
|
|
478
|
+
# Write GaussianModel frames
|
|
479
|
+
writer = FrameWriter(
|
|
480
|
+
first_frame_path="output/frame_0000.ply",
|
|
481
|
+
subsequent_format="output/frame_{:04d}.ply",
|
|
482
|
+
start_index=1,
|
|
483
|
+
)
|
|
484
|
+
writer.write(frame_iterator)
|
|
485
|
+
|
|
486
|
+
# Read/write bytes
|
|
487
|
+
bytes_reader = BytesReader("compressed.bin", chunk_size=65536)
|
|
488
|
+
bytes_writer = BytesWriter("output.bin")
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
## Creating a Custom Codec
|
|
492
|
+
|
|
493
|
+
To create a custom inter-frame codec:
|
|
494
|
+
|
|
495
|
+
### 1. Define Payload Classes
|
|
496
|
+
|
|
497
|
+
```python
|
|
498
|
+
from dataclasses import dataclass
|
|
499
|
+
from gsvvcompressor import Payload
|
|
500
|
+
|
|
501
|
+
@dataclass
|
|
502
|
+
class MyKeyframePayload(Payload):
|
|
503
|
+
# Full frame data
|
|
504
|
+
data: torch.Tensor
|
|
505
|
+
|
|
506
|
+
def to(self, device):
|
|
507
|
+
return MyKeyframePayload(data=self.data.to(device))
|
|
508
|
+
|
|
509
|
+
@dataclass
|
|
510
|
+
class MyInterframePayload(Payload):
|
|
511
|
+
# Delta data only
|
|
512
|
+
mask: torch.Tensor
|
|
513
|
+
delta: torch.Tensor
|
|
514
|
+
|
|
515
|
+
def to(self, device):
|
|
516
|
+
return MyInterframePayload(
|
|
517
|
+
mask=self.mask.to(device),
|
|
518
|
+
delta=self.delta.to(device),
|
|
519
|
+
)
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### 2. Define Context and Config
|
|
523
|
+
|
|
524
|
+
```python
|
|
525
|
+
from gsvvcompressor.interframe import InterframeCodecContext, InterframeEncoderInitConfig
|
|
526
|
+
|
|
527
|
+
@dataclass
|
|
528
|
+
class MyCodecConfig(InterframeEncoderInitConfig):
|
|
529
|
+
quality: int = 10
|
|
530
|
+
|
|
531
|
+
@dataclass
|
|
532
|
+
class MyCodecContext(InterframeCodecContext):
|
|
533
|
+
encoded_data: torch.Tensor
|
|
534
|
+
quality: int
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### 3. Implement the Interface
|
|
538
|
+
|
|
539
|
+
```python
|
|
540
|
+
from gsvvcompressor.interframe import InterframeCodecInterface
|
|
541
|
+
|
|
542
|
+
class MyCodecInterface(InterframeCodecInterface):
|
|
543
|
+
def keyframe_to_context(self, frame, init_config):
|
|
544
|
+
# Encode frame to context
|
|
545
|
+
return MyCodecContext(
|
|
546
|
+
encoded_data=encode(frame, init_config.quality),
|
|
547
|
+
quality=init_config.quality,
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
def interframe_to_context(self, frame, prev_context):
|
|
551
|
+
# Encode frame using previous context
|
|
552
|
+
return MyCodecContext(
|
|
553
|
+
encoded_data=encode(frame, prev_context.quality),
|
|
554
|
+
quality=prev_context.quality,
|
|
555
|
+
)
|
|
556
|
+
|
|
557
|
+
def encode_keyframe(self, context):
|
|
558
|
+
return MyKeyframePayload(data=context.encoded_data)
|
|
559
|
+
|
|
560
|
+
def encode_interframe(self, prev_context, next_context):
|
|
561
|
+
# Compute delta
|
|
562
|
+
mask = prev_context.encoded_data != next_context.encoded_data
|
|
563
|
+
delta = next_context.encoded_data[mask]
|
|
564
|
+
return MyInterframePayload(mask=mask, delta=delta)
|
|
565
|
+
|
|
566
|
+
def decode_keyframe(self, payload):
|
|
567
|
+
return MyCodecContext(encoded_data=payload.data, quality=0)
|
|
568
|
+
|
|
569
|
+
def decode_interframe(self, payload, prev_context):
|
|
570
|
+
new_data = prev_context.encoded_data.clone()
|
|
571
|
+
new_data[payload.mask] = payload.delta
|
|
572
|
+
return MyCodecContext(encoded_data=new_data, quality=prev_context.quality)
|
|
573
|
+
|
|
574
|
+
def context_to_frame(self, context, frame):
|
|
575
|
+
# Decode context to frame
|
|
576
|
+
frame._xyz = decode(context.encoded_data)
|
|
577
|
+
return frame
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### 4. Build the Encoder/Decoder
|
|
581
|
+
|
|
582
|
+
```python
|
|
583
|
+
from gsvvcompressor.interframe import InterframeEncoder, InterframeDecoder
|
|
584
|
+
from gsvvcompressor.compress.zstd import ZstdSerializer, ZstdDeserializer
|
|
585
|
+
|
|
586
|
+
def build_my_encoder(config):
|
|
587
|
+
return InterframeEncoder(
|
|
588
|
+
serializer=ZstdSerializer(level=7),
|
|
589
|
+
interface=MyCodecInterface(),
|
|
590
|
+
init_config=config,
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
def build_my_decoder(config):
|
|
594
|
+
return InterframeDecoder(
|
|
595
|
+
deserializer=ZstdDeserializer(),
|
|
596
|
+
interface=MyCodecInterface(),
|
|
597
|
+
)
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### 5. Register (Optional)
|
|
601
|
+
|
|
602
|
+
```python
|
|
603
|
+
from gsvvcompressor.combinations import register_encoder, register_decoder
|
|
604
|
+
|
|
605
|
+
register_encoder("mycodec", build_my_encoder, MyCodecConfig, "My custom codec")
|
|
606
|
+
register_decoder("mycodec", build_my_decoder, MyCodecConfig, "My custom codec")
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## Project Structure
|
|
610
|
+
|
|
611
|
+
```
|
|
612
|
+
gsvvcompressor/
|
|
613
|
+
├── __init__.py # Core exports: Payload, AbstractEncoder/Decoder, AbstractSerializer/Deserializer
|
|
614
|
+
├── __main__.py # CLI entry point
|
|
615
|
+
├── payload.py # Payload abstract base class
|
|
616
|
+
├── encoder.py # AbstractEncoder base class
|
|
617
|
+
├── decoder.py # AbstractDecoder base class
|
|
618
|
+
├── serializer.py # AbstractSerializer base class
|
|
619
|
+
├── deserializer.py # AbstractDeserializer base class
|
|
620
|
+
├── combinations/ # Pre-built codec combinations
|
|
621
|
+
│ ├── __init__.py
|
|
622
|
+
│ ├── registry.py # Encoder/decoder registry
|
|
623
|
+
│ └── vq_xyz_zstd.py # VQ + XYZ + Zstd combination
|
|
624
|
+
├── compress/ # Compression implementations
|
|
625
|
+
│ ├── __init__.py
|
|
626
|
+
│ └── zstd.py # Zstd serializer/deserializer
|
|
627
|
+
├── interframe/ # Inter-frame compression infrastructure
|
|
628
|
+
│ ├── __init__.py
|
|
629
|
+
│ ├── interface.py # InterframeCodecInterface and related classes
|
|
630
|
+
│ ├── encoder.py # InterframeEncoder
|
|
631
|
+
│ ├── decoder.py # InterframeDecoder
|
|
632
|
+
│ ├── combine.py # CombinedInterframeCodecInterface
|
|
633
|
+
│ └── twopass.py # Two-pass encoding utilities
|
|
634
|
+
├── io/ # Input/output utilities
|
|
635
|
+
│ ├── __init__.py
|
|
636
|
+
│ ├── bytes.py # BytesReader/Writer
|
|
637
|
+
│ ├── config.py # Configuration dataclasses
|
|
638
|
+
│ └── gaussian_model.py # FrameReader/Writer
|
|
639
|
+
├── vq/ # Vector quantization codec
|
|
640
|
+
│ ├── __init__.py
|
|
641
|
+
│ ├── interface.py # VQInterframeCodecInterface
|
|
642
|
+
│ └── twopass.py # Two-pass VQ utilities
|
|
643
|
+
└── xyz/ # XYZ coordinate codec
|
|
644
|
+
├── __init__.py
|
|
645
|
+
├── interface.py # XYZQuantInterframeCodecInterface
|
|
646
|
+
├── quant.py # Quantization functions
|
|
647
|
+
└── ... # Other XYZ utilities
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
## Class Hierarchy
|
|
651
|
+
|
|
652
|
+
```
|
|
653
|
+
Payload (ABC)
|
|
654
|
+
├── VQKeyframePayload
|
|
655
|
+
├── VQInterframePayload
|
|
656
|
+
├── XYZQuantKeyframePayload
|
|
657
|
+
├── XYZQuantInterframePayload
|
|
658
|
+
└── CombinedPayload
|
|
659
|
+
|
|
660
|
+
AbstractEncoder (ABC)
|
|
661
|
+
└── InterframeEncoder
|
|
662
|
+
|
|
663
|
+
AbstractDecoder (ABC)
|
|
664
|
+
└── InterframeDecoder
|
|
665
|
+
|
|
666
|
+
AbstractSerializer (ABC)
|
|
667
|
+
└── ZstdSerializer
|
|
668
|
+
|
|
669
|
+
AbstractDeserializer (ABC)
|
|
670
|
+
└── ZstdDeserializer
|
|
671
|
+
|
|
672
|
+
InterframeCodecInterface (ABC)
|
|
673
|
+
├── VQInterframeCodecInterface
|
|
674
|
+
├── XYZQuantInterframeCodecInterface
|
|
675
|
+
└── CombinedInterframeCodecInterface
|
|
676
|
+
|
|
677
|
+
InterframeEncoderInitConfig
|
|
678
|
+
├── VQInterframeCodecConfig
|
|
679
|
+
├── XYZQuantInterframeCodecConfig
|
|
680
|
+
└── CombinedInterframeEncoderInitConfig
|
|
681
|
+
|
|
682
|
+
InterframeCodecContext
|
|
683
|
+
├── VQInterframeCodecContext
|
|
684
|
+
├── XYZQuantInterframeCodecContext
|
|
685
|
+
└── CombinedInterframeCodecContext
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
## License
|
|
689
|
+
|
|
690
|
+
See [LICENSE](LICENSE) for details.
|