gsvvcompressor 1.2.0__cp311-cp311-macosx_10_9_universal2.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.
Files changed (50) hide show
  1. gsvvcompressor/__init__.py +13 -0
  2. gsvvcompressor/__main__.py +243 -0
  3. gsvvcompressor/combinations/__init__.py +84 -0
  4. gsvvcompressor/combinations/registry.py +52 -0
  5. gsvvcompressor/combinations/vq_xyz_1mask.py +89 -0
  6. gsvvcompressor/combinations/vq_xyz_1mask_zstd.py +103 -0
  7. gsvvcompressor/combinations/vq_xyz_draco.py +468 -0
  8. gsvvcompressor/combinations/vq_xyz_draco_2pass.py +156 -0
  9. gsvvcompressor/combinations/vq_xyz_zstd.py +106 -0
  10. gsvvcompressor/compress/__init__.py +5 -0
  11. gsvvcompressor/compress/zstd.py +144 -0
  12. gsvvcompressor/decoder.py +155 -0
  13. gsvvcompressor/deserializer.py +42 -0
  14. gsvvcompressor/draco/__init__.py +34 -0
  15. gsvvcompressor/draco/draco_decoder +0 -0
  16. gsvvcompressor/draco/draco_encoder +0 -0
  17. gsvvcompressor/draco/dracoreduced3dgs.cpython-311-darwin.so +0 -0
  18. gsvvcompressor/draco/interface.py +339 -0
  19. gsvvcompressor/draco/serialize.py +235 -0
  20. gsvvcompressor/draco/twopass.py +359 -0
  21. gsvvcompressor/encoder.py +122 -0
  22. gsvvcompressor/interframe/__init__.py +11 -0
  23. gsvvcompressor/interframe/combine.py +271 -0
  24. gsvvcompressor/interframe/decoder.py +99 -0
  25. gsvvcompressor/interframe/encoder.py +92 -0
  26. gsvvcompressor/interframe/interface.py +221 -0
  27. gsvvcompressor/interframe/twopass.py +226 -0
  28. gsvvcompressor/io/__init__.py +31 -0
  29. gsvvcompressor/io/bytes.py +103 -0
  30. gsvvcompressor/io/config.py +78 -0
  31. gsvvcompressor/io/gaussian_model.py +127 -0
  32. gsvvcompressor/movecameras.py +33 -0
  33. gsvvcompressor/payload.py +34 -0
  34. gsvvcompressor/serializer.py +42 -0
  35. gsvvcompressor/vq/__init__.py +15 -0
  36. gsvvcompressor/vq/interface.py +324 -0
  37. gsvvcompressor/vq/singlemask.py +127 -0
  38. gsvvcompressor/vq/twopass.py +1 -0
  39. gsvvcompressor/xyz/__init__.py +26 -0
  40. gsvvcompressor/xyz/dense.py +39 -0
  41. gsvvcompressor/xyz/interface.py +382 -0
  42. gsvvcompressor/xyz/knn.py +141 -0
  43. gsvvcompressor/xyz/quant.py +143 -0
  44. gsvvcompressor/xyz/size.py +44 -0
  45. gsvvcompressor/xyz/twopass.py +1 -0
  46. gsvvcompressor-1.2.0.dist-info/METADATA +690 -0
  47. gsvvcompressor-1.2.0.dist-info/RECORD +50 -0
  48. gsvvcompressor-1.2.0.dist-info/WHEEL +5 -0
  49. gsvvcompressor-1.2.0.dist-info/licenses/LICENSE +21 -0
  50. gsvvcompressor-1.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,106 @@
1
+ """
2
+ VQ + XYZ quantization + Zstd compression encoder/decoder combination.
3
+ """
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Optional
7
+
8
+ from ..compress.zstd import ZstdSerializer, ZstdDeserializer
9
+ from ..interframe.combine import (
10
+ CombinedInterframeCodecInterface,
11
+ CombinedInterframeEncoderInitConfig,
12
+ )
13
+ from ..interframe.encoder import InterframeEncoder
14
+ from ..interframe.decoder import InterframeDecoder
15
+ from ..vq.interface import VQInterframeCodecInterface, VQInterframeCodecConfig
16
+ from ..xyz.interface import XYZQuantInterframeCodecInterface, XYZQuantInterframeCodecConfig
17
+ from .registry import register_encoder, register_decoder
18
+
19
+
20
+ def VQXYZZstdEncoder(
21
+ vq_config: VQInterframeCodecConfig,
22
+ xyz_config: XYZQuantInterframeCodecConfig,
23
+ zstd_level: int = 7,
24
+ payload_device: Optional[str] = None,
25
+ ) -> InterframeEncoder:
26
+ """Create an encoder combining VQ + XYZ quantization + Zstd compression."""
27
+ vq_interface = VQInterframeCodecInterface()
28
+ xyz_interface = XYZQuantInterframeCodecInterface()
29
+ combined_interface = CombinedInterframeCodecInterface([xyz_interface, vq_interface])
30
+ combined_config = CombinedInterframeEncoderInitConfig(
31
+ init_configs=[xyz_config, vq_config]
32
+ )
33
+ serializer = ZstdSerializer(level=zstd_level)
34
+ return InterframeEncoder(
35
+ serializer=serializer,
36
+ interface=combined_interface,
37
+ init_config=combined_config,
38
+ payload_device=payload_device,
39
+ )
40
+
41
+
42
+ def VQXYZZstdDecoder(
43
+ payload_device: Optional[str] = None,
44
+ device: Optional[str] = None,
45
+ ) -> InterframeDecoder:
46
+ """Create a decoder for VQ + XYZ quantization + Zstd compressed data."""
47
+ vq_interface = VQInterframeCodecInterface()
48
+ xyz_interface = XYZQuantInterframeCodecInterface()
49
+ combined_interface = CombinedInterframeCodecInterface([xyz_interface, vq_interface])
50
+ deserializer = ZstdDeserializer()
51
+ return InterframeDecoder(
52
+ deserializer=deserializer,
53
+ interface=combined_interface,
54
+ payload_device=payload_device,
55
+ device=device,
56
+ )
57
+
58
+
59
+ @dataclass
60
+ class VQXYZZstdEncoderConfig:
61
+ """Configuration for VQ + XYZ + Zstd encoder."""
62
+ vq: VQInterframeCodecConfig = field(default_factory=VQInterframeCodecConfig)
63
+ xyz: XYZQuantInterframeCodecConfig = field(default_factory=XYZQuantInterframeCodecConfig)
64
+ zstd_level: int = 7
65
+ payload_device: Optional[str] = None
66
+
67
+
68
+ @dataclass
69
+ class VQXYZZstdDecoderConfig:
70
+ """Configuration for VQ + XYZ + Zstd decoder."""
71
+ payload_device: Optional[str] = None
72
+ device: Optional[str] = None
73
+
74
+
75
+ def build_vqxyzzstd_encoder(config: VQXYZZstdEncoderConfig) -> InterframeEncoder:
76
+ """Build encoder from configuration."""
77
+ return VQXYZZstdEncoder(
78
+ vq_config=config.vq,
79
+ xyz_config=config.xyz,
80
+ zstd_level=config.zstd_level,
81
+ payload_device=config.payload_device,
82
+ )
83
+
84
+
85
+ def build_vqxyzzstd_decoder(config: VQXYZZstdDecoderConfig) -> InterframeDecoder:
86
+ """Build decoder from configuration."""
87
+ return VQXYZZstdDecoder(
88
+ payload_device=config.payload_device,
89
+ device=config.device,
90
+ )
91
+
92
+
93
+ # Register
94
+ register_encoder(
95
+ "vqxyzzstd",
96
+ build_vqxyzzstd_encoder,
97
+ VQXYZZstdEncoderConfig,
98
+ "VQ + XYZ quantization + Zstd compression",
99
+ )
100
+
101
+ register_decoder(
102
+ "vqxyzzstd",
103
+ build_vqxyzzstd_decoder,
104
+ VQXYZZstdDecoderConfig,
105
+ "VQ + XYZ quantization + Zstd decompression",
106
+ )
@@ -0,0 +1,5 @@
1
+ """Zstandard-based streaming serialization and deserialization."""
2
+
3
+ from .zstd import ZstdDeserializer, ZstdSerializer
4
+
5
+ __all__ = ["ZstdSerializer", "ZstdDeserializer"]
@@ -0,0 +1,144 @@
1
+ """
2
+ Zstandard-based streaming serialization and deserialization.
3
+
4
+ Uses cloudpickle + length-prefix framing + zstd compression for efficient
5
+ streaming of Payload objects. This approach can serialize almost anything
6
+ including nested custom classes, torch.Tensor, and numpy.ndarray.
7
+
8
+ WARNING: cloudpickle uses pickle under the hood. Do NOT use with untrusted
9
+ data sources. Only use with trusted data (local files, same-process, etc.).
10
+ """
11
+
12
+ import struct
13
+ from typing import Iterator
14
+
15
+ import cloudpickle as pickle
16
+ import zstandard as zstd
17
+
18
+ from ..deserializer import AbstractDeserializer
19
+ from ..payload import Payload
20
+ from ..serializer import AbstractSerializer
21
+
22
+ # 4-byte big-endian length prefix (max 4GB per object)
23
+ _LEN = struct.Struct(">I")
24
+
25
+
26
+ class ZstdSerializer(AbstractSerializer):
27
+ """
28
+ Streaming serializer using cloudpickle + length-prefix framing + zstd.
29
+
30
+ Each Payload is pickled, prefixed with its length, then compressed
31
+ incrementally using zstd streaming compression.
32
+ """
33
+
34
+ def __init__(self, level: int = 7):
35
+ """
36
+ Initialize the serializer.
37
+
38
+ Args:
39
+ level: Zstd compression level (1-22). Default is 7.
40
+ """
41
+ self._compressor = zstd.ZstdCompressor(level=level).compressobj()
42
+
43
+ def serialize_frame(self, payload: Payload) -> Iterator[bytes]:
44
+ """
45
+ Serialize and compress a Payload object.
46
+
47
+ Args:
48
+ payload: A Payload instance to serialize.
49
+
50
+ Yields:
51
+ Compressed byte chunks (may yield zero chunks if buffered).
52
+ """
53
+ # Pickle the payload
54
+ pickled = pickle.dumps(payload, protocol=pickle.DEFAULT_PROTOCOL)
55
+ # Add length prefix framing
56
+ framed = _LEN.pack(len(pickled)) + pickled
57
+ # Compress incrementally
58
+ out = self._compressor.compress(framed)
59
+ if out:
60
+ yield out
61
+
62
+ def flush(self) -> Iterator[bytes]:
63
+ """
64
+ Flush remaining compressed data.
65
+
66
+ Yields:
67
+ Final compressed byte chunks.
68
+ """
69
+ tail = self._compressor.flush()
70
+ if tail:
71
+ yield tail
72
+
73
+
74
+ class ZstdDeserializer(AbstractDeserializer):
75
+ """
76
+ Streaming deserializer using zstd + length-prefix framing + cloudpickle.
77
+
78
+ Decompresses incoming bytes incrementally, buffers until a complete
79
+ length-prefixed frame is available, then unpickles and yields Payloads.
80
+ """
81
+
82
+ def __init__(self):
83
+ """Initialize the deserializer."""
84
+ self._decompressor = zstd.ZstdDecompressor().decompressobj()
85
+ self._buffer = bytearray()
86
+
87
+ def deserialize_frame(self, data: bytes) -> Iterator[Payload]:
88
+ """
89
+ Decompress and deserialize bytes to Payload objects.
90
+
91
+ Args:
92
+ data: Compressed bytes to deserialize.
93
+
94
+ Yields:
95
+ Complete Payload objects as they become available.
96
+ """
97
+ # Decompress and add to buffer
98
+ decompressed = self._decompressor.decompress(data)
99
+ if decompressed:
100
+ self._buffer.extend(decompressed)
101
+
102
+ # Yield complete frames
103
+ yield from self._extract_payloads()
104
+
105
+ def flush(self) -> Iterator[Payload]:
106
+ """
107
+ Flush any remaining buffered data.
108
+
109
+ Yields:
110
+ Any remaining Payload objects in the buffer.
111
+ """
112
+ # Try to extract any remaining complete payloads
113
+ yield from self._extract_payloads()
114
+
115
+ # If there's leftover data, it's incomplete/corrupted
116
+ if self._buffer:
117
+ raise ValueError(
118
+ f"Incomplete data in buffer: {len(self._buffer)} bytes remaining"
119
+ )
120
+
121
+ def _extract_payloads(self) -> Iterator[Payload]:
122
+ """
123
+ Extract complete payloads from the buffer.
124
+
125
+ Yields:
126
+ Complete Payload objects.
127
+ """
128
+ while True:
129
+ # Need at least length prefix
130
+ if len(self._buffer) < _LEN.size:
131
+ break
132
+
133
+ # Read length
134
+ (length,) = _LEN.unpack(self._buffer[: _LEN.size])
135
+
136
+ # Check if we have complete frame
137
+ if len(self._buffer) < _LEN.size + length:
138
+ break
139
+
140
+ # Extract and unpickle payload
141
+ pickled = bytes(self._buffer[_LEN.size: _LEN.size + length])
142
+ del self._buffer[: _LEN.size + length]
143
+
144
+ yield pickle.loads(pickled)
@@ -0,0 +1,155 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Iterator, Self
3
+
4
+ import torch
5
+
6
+ from gaussian_splatting import GaussianModel
7
+
8
+ from .deserializer import AbstractDeserializer
9
+ from .payload import Payload
10
+
11
+
12
+ class AbstractDecoder(ABC):
13
+ """
14
+ Abstract base class for decompression algorithms.
15
+
16
+ This decoder uses a two-stage process:
17
+ 1. Deserialize bytes to Payload objects (via the deserializer)
18
+ 2. Unpack Payloads to frames (via `unpack`)
19
+
20
+ Subclasses must implement `unpack` and `flush_unpack`, and provide
21
+ a deserializer. This design separates frame unpacking logic from
22
+ deserialization format.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ deserializer: AbstractDeserializer,
28
+ payload_device: str | torch.device | None = None,
29
+ device: str | torch.device | None = None,
30
+ ):
31
+ """
32
+ Initialize the decoder.
33
+
34
+ Args:
35
+ deserializer: The deserializer to use for converting bytes to Payload.
36
+ payload_device: The target device for input Payloads before
37
+ unpacking (e.g., 'cpu', 'cuda'). If None, no device
38
+ transfer is performed.
39
+ device: The target device for output GaussianModel frames
40
+ (e.g., 'cpu', 'cuda'). If None, no device transfer is performed.
41
+ """
42
+ self._deserializer = deserializer
43
+ self._payload_device = payload_device
44
+ self._device = device
45
+
46
+ def to(self, device: str | torch.device | None) -> Self:
47
+ """Set the device for decoded models.
48
+
49
+ Args:
50
+ device: The device to move models to after decoding.
51
+ Can be a string like "cuda" or "cpu", a torch.device, or None.
52
+
53
+ Returns:
54
+ self for method chaining.
55
+ """
56
+ self._device = device
57
+ return self
58
+
59
+ @abstractmethod
60
+ def unpack(self, payload: Payload) -> Iterator[GaussianModel]:
61
+ """
62
+ Unpack frame(s) from a Payload.
63
+
64
+ This method reconstructs the original frame(s) from the Payload object.
65
+
66
+ Args:
67
+ payload: A Payload instance to unpack.
68
+
69
+ Yields:
70
+ Unpacked GaussianModel instances. May yield zero, one, or multiple
71
+ models. When the iterator is exhausted, all frames for this payload
72
+ have been yielded.
73
+ """
74
+ pass
75
+
76
+ @abstractmethod
77
+ def flush_unpack(self) -> Iterator[GaussianModel]:
78
+ """
79
+ Flush any remaining buffered frames from the unpacking stage.
80
+
81
+ This method should be called after all payloads have been unpacked
82
+ to ensure any remaining buffered frames are output.
83
+
84
+ Yields:
85
+ Remaining buffered GaussianModel instances. May yield zero, one,
86
+ or multiple models until all buffered data has been flushed.
87
+ """
88
+ pass
89
+
90
+ def decode_frame(self, data: bytes) -> Iterator[GaussianModel]:
91
+ """
92
+ Decode a single chunk of compressed data.
93
+
94
+ This method deserializes the bytes to Payloads and then unpacks
95
+ the original frames.
96
+
97
+ Args:
98
+ data: A bytes object containing compressed data to decode.
99
+
100
+ Yields:
101
+ Decoded GaussianModel instances. May yield zero, one, or multiple
102
+ models. When the iterator is exhausted, all frames for this data
103
+ chunk have been decoded.
104
+ """
105
+ for payload in self._deserializer.deserialize_frame(data):
106
+ if self._payload_device is not None:
107
+ payload = payload.to(self._payload_device)
108
+ for model in self.unpack(payload):
109
+ if self._device is not None:
110
+ model.to(self._device)
111
+ yield model
112
+
113
+ def flush(self) -> Iterator[GaussianModel]:
114
+ """
115
+ Flush any remaining buffered data from both deserialization and unpacking.
116
+
117
+ This method should be called after all data has been decoded
118
+ to ensure any remaining buffered frames are output.
119
+
120
+ Yields:
121
+ Remaining buffered GaussianModel instances. May yield zero, one,
122
+ or multiple models until all buffered data has been flushed.
123
+ """
124
+ # Flush deserialization stage and unpack any remaining payloads
125
+ for payload in self._deserializer.flush():
126
+ if self._payload_device is not None:
127
+ payload = payload.to(self._payload_device)
128
+ for model in self.unpack(payload):
129
+ if self._device is not None:
130
+ model.to(self._device)
131
+ yield model
132
+
133
+ # Flush unpacking stage
134
+ for model in self.flush_unpack():
135
+ if self._device is not None:
136
+ model.to(self._device)
137
+ yield model
138
+
139
+ def decode_stream(self, stream: Iterator[bytes]) -> Iterator[GaussianModel]:
140
+ """
141
+ Decode a stream of compressed byte chunks into GaussianModel frames.
142
+
143
+ This method processes each chunk, deserializing and unpacking frames.
144
+ It handles the flush logic for both deserialization and unpacking stages.
145
+
146
+ Args:
147
+ stream: An iterator that yields bytes objects to decode.
148
+
149
+ Yields:
150
+ Decoded GaussianModel instances for each processed chunk or flush.
151
+ """
152
+ for data in stream:
153
+ yield from self.decode_frame(data)
154
+
155
+ yield from self.flush()
@@ -0,0 +1,42 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Iterator
3
+
4
+ from .payload import Payload
5
+
6
+
7
+ class AbstractDeserializer(ABC):
8
+ """
9
+ Abstract base class for deserializing bytes to Payload objects.
10
+
11
+ Subclasses must implement `deserialize_frame` and `flush` methods to define
12
+ the specific deserialization logic for byte sequences into Payload.
13
+ """
14
+
15
+ @abstractmethod
16
+ def deserialize_frame(self, data: bytes) -> Iterator[Payload]:
17
+ """
18
+ Deserialize a chunk of bytes to Payload objects.
19
+
20
+ Args:
21
+ data: A bytes object containing serialized data to deserialize.
22
+
23
+ Yields:
24
+ Deserialized Payload instances. May yield zero, one, or multiple
25
+ payloads depending on available data. When the iterator is exhausted,
26
+ all complete payloads from this data chunk have been yielded.
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ def flush(self) -> Iterator[Payload]:
32
+ """
33
+ Flush any remaining buffered data.
34
+
35
+ This method should be called after all data has been deserialized
36
+ to ensure any remaining buffered payloads are output.
37
+
38
+ Yields:
39
+ Remaining buffered Payload instances. May yield zero, one, or
40
+ multiple payloads until all buffered data has been flushed.
41
+ """
42
+ pass
@@ -0,0 +1,34 @@
1
+ """
2
+ Draco-based compression for reduced 3DGS data.
3
+
4
+ This module provides:
5
+ - DracoPayload: Payload structure for Draco-compatible data
6
+ - TwoPassDracoPayload: DracoPayload with num_points field for two-pass serialization
7
+ - DracoInterframeCodecTranscodingInterface: Abstract interface for payload transcoding
8
+ - DracoInterframeCodecInterface: Wrapper codec that transcodes to/from DracoPayload
9
+ - DracoSerializer/DracoDeserializer: Streaming serialization using Draco encoding
10
+ - TwoPassDracoSerializer/TwoPassDracoDeserializer: Two-pass serialization for batched compression
11
+ """
12
+
13
+ from .interface import (
14
+ DracoInterframeCodecInterface,
15
+ DracoInterframeCodecTranscodingInterface,
16
+ DracoPayload,
17
+ )
18
+ from .serialize import DracoDeserializer, DracoSerializer
19
+ from .twopass import (
20
+ TwoPassDracoDeserializer,
21
+ TwoPassDracoPayload,
22
+ TwoPassDracoSerializer,
23
+ )
24
+
25
+ __all__ = [
26
+ "DracoPayload",
27
+ "TwoPassDracoPayload",
28
+ "DracoInterframeCodecTranscodingInterface",
29
+ "DracoInterframeCodecInterface",
30
+ "DracoSerializer",
31
+ "DracoDeserializer",
32
+ "TwoPassDracoSerializer",
33
+ "TwoPassDracoDeserializer",
34
+ ]
Binary file
Binary file