gsvvcompressor 1.2.0__cp311-cp311-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.cp311-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,271 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import List, Self
|
|
3
|
+
|
|
4
|
+
from gaussian_splatting import GaussianModel
|
|
5
|
+
|
|
6
|
+
from ..payload import Payload
|
|
7
|
+
from .interface import (
|
|
8
|
+
InterframeCodecInterface,
|
|
9
|
+
InterframeCodecContext,
|
|
10
|
+
InterframeEncoderInitConfig,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class CombinedPayload(Payload):
|
|
16
|
+
"""
|
|
17
|
+
Combined payload containing multiple sub-codec payloads.
|
|
18
|
+
"""
|
|
19
|
+
payloads: List[Payload]
|
|
20
|
+
|
|
21
|
+
def to(self, device) -> Self:
|
|
22
|
+
"""
|
|
23
|
+
Move the Payload to the specified device.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
device: The target device (e.g., 'cpu', 'cuda', torch.device).
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
A new CombinedPayload instance with all sub-payloads on the target device.
|
|
30
|
+
"""
|
|
31
|
+
return CombinedPayload(
|
|
32
|
+
payloads=[p.to(device) for p in self.payloads],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class CombinedInterframeEncoderInitConfig(InterframeEncoderInitConfig):
|
|
38
|
+
"""
|
|
39
|
+
Combined encoder initialization configuration containing multiple sub-codec configs.
|
|
40
|
+
"""
|
|
41
|
+
init_configs: List[InterframeEncoderInitConfig]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class CombinedInterframeCodecContext(InterframeCodecContext):
|
|
46
|
+
"""
|
|
47
|
+
Combined context containing multiple sub-codec contexts.
|
|
48
|
+
"""
|
|
49
|
+
contexts: List[InterframeCodecContext]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class CombinedInterframeCodecInterface(InterframeCodecInterface):
|
|
53
|
+
"""
|
|
54
|
+
A codec that combines multiple InterframeCodecInterface instances.
|
|
55
|
+
|
|
56
|
+
Each method calls the corresponding method on all sub-codecs and combines
|
|
57
|
+
their outputs.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, interfaces: List[InterframeCodecInterface]):
|
|
61
|
+
"""
|
|
62
|
+
Initialize the combined codec with a list of sub-codecs.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
interfaces: List of InterframeCodecInterface instances to combine.
|
|
66
|
+
"""
|
|
67
|
+
if not interfaces:
|
|
68
|
+
raise ValueError("At least one interface must be provided")
|
|
69
|
+
self.interfaces = interfaces
|
|
70
|
+
|
|
71
|
+
def decode_interframe(
|
|
72
|
+
self, payload: CombinedPayload, prev_context: CombinedInterframeCodecContext
|
|
73
|
+
) -> CombinedInterframeCodecContext:
|
|
74
|
+
"""
|
|
75
|
+
Decode a delta payload to reconstruct the next frame's context.
|
|
76
|
+
|
|
77
|
+
Calls decode_interframe on all sub-codecs and combines their contexts.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
payload: The delta payload containing individual payloads for each sub-codec.
|
|
81
|
+
prev_context: The context of the previous frame containing individual contexts for each sub-codec.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A CombinedInterframeCodecContext containing all sub-codec contexts.
|
|
85
|
+
"""
|
|
86
|
+
# Call decode_interframe on each sub-codec
|
|
87
|
+
contexts = []
|
|
88
|
+
for interface, payload, prev_context in zip(self.interfaces, payload.payloads, prev_context.contexts):
|
|
89
|
+
context = interface.decode_interframe(payload, prev_context)
|
|
90
|
+
contexts.append(context)
|
|
91
|
+
return CombinedInterframeCodecContext(contexts=contexts)
|
|
92
|
+
|
|
93
|
+
def encode_interframe(
|
|
94
|
+
self,
|
|
95
|
+
prev_context: CombinedInterframeCodecContext,
|
|
96
|
+
next_context: CombinedInterframeCodecContext,
|
|
97
|
+
) -> CombinedPayload:
|
|
98
|
+
"""
|
|
99
|
+
Encode the difference between two consecutive frames.
|
|
100
|
+
|
|
101
|
+
Calls encode_interframe on all sub-codecs and combines their payloads.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
prev_context: The context of the previous frame containing individual contexts for each sub-codec.
|
|
105
|
+
next_context: The context of the next frame containing individual contexts for each sub-codec.
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
A CombinedPayload containing all sub-codec payloads.
|
|
109
|
+
"""
|
|
110
|
+
# Call encode_interframe on each sub-codec
|
|
111
|
+
payloads = []
|
|
112
|
+
for interface, prev_context, next_context in zip(self.interfaces, prev_context.contexts, next_context.contexts):
|
|
113
|
+
payload = interface.encode_interframe(prev_context, next_context)
|
|
114
|
+
payloads.append(payload)
|
|
115
|
+
return CombinedPayload(payloads=payloads)
|
|
116
|
+
|
|
117
|
+
def decode_keyframe(self, payload: CombinedPayload) -> CombinedInterframeCodecContext:
|
|
118
|
+
"""
|
|
119
|
+
Decode a keyframe payload to create initial context.
|
|
120
|
+
|
|
121
|
+
Calls decode_keyframe on all sub-codecs and combines their contexts.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
payload: The keyframe payload containing individual payloads for each sub-codec.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
A CombinedInterframeCodecContext containing all sub-codec contexts.
|
|
128
|
+
"""
|
|
129
|
+
# Call decode_keyframe on each sub-codec
|
|
130
|
+
contexts = []
|
|
131
|
+
for interface, payload in zip(self.interfaces, payload.payloads):
|
|
132
|
+
context = interface.decode_keyframe(payload)
|
|
133
|
+
contexts.append(context)
|
|
134
|
+
return CombinedInterframeCodecContext(contexts=contexts)
|
|
135
|
+
|
|
136
|
+
def decode_keyframe_for_encode(
|
|
137
|
+
self, payload: CombinedPayload, context: CombinedInterframeCodecContext
|
|
138
|
+
) -> CombinedInterframeCodecContext:
|
|
139
|
+
"""
|
|
140
|
+
Decode a keyframe payload during encoding to avoid error accumulation.
|
|
141
|
+
|
|
142
|
+
Calls decode_keyframe_for_encode on all sub-codecs and combines their contexts.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
payload: The keyframe payload containing individual payloads for each sub-codec.
|
|
146
|
+
context: The original context containing individual contexts for each sub-codec.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
A CombinedInterframeCodecContext containing all sub-codec reconstructed contexts.
|
|
150
|
+
"""
|
|
151
|
+
# Call decode_keyframe_for_encode on each sub-codec
|
|
152
|
+
contexts = []
|
|
153
|
+
for interface, sub_payload, sub_context in zip(
|
|
154
|
+
self.interfaces, payload.payloads, context.contexts
|
|
155
|
+
):
|
|
156
|
+
reconstructed = interface.decode_keyframe_for_encode(sub_payload, sub_context)
|
|
157
|
+
contexts.append(reconstructed)
|
|
158
|
+
return CombinedInterframeCodecContext(contexts=contexts)
|
|
159
|
+
|
|
160
|
+
def decode_interframe_for_encode(
|
|
161
|
+
self, payload: CombinedPayload, prev_context: CombinedInterframeCodecContext
|
|
162
|
+
) -> CombinedInterframeCodecContext:
|
|
163
|
+
"""
|
|
164
|
+
Decode an interframe payload during encoding to avoid error accumulation.
|
|
165
|
+
|
|
166
|
+
Calls decode_interframe_for_encode on all sub-codecs and combines their contexts.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
payload: The interframe payload containing individual payloads for each sub-codec.
|
|
170
|
+
prev_context: The previous context containing individual contexts for each sub-codec.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
A CombinedInterframeCodecContext containing all sub-codec reconstructed contexts.
|
|
174
|
+
"""
|
|
175
|
+
# Call decode_interframe_for_encode on each sub-codec
|
|
176
|
+
contexts = []
|
|
177
|
+
for interface, sub_payload, sub_prev_context in zip(
|
|
178
|
+
self.interfaces, payload.payloads, prev_context.contexts
|
|
179
|
+
):
|
|
180
|
+
reconstructed = interface.decode_interframe_for_encode(sub_payload, sub_prev_context)
|
|
181
|
+
contexts.append(reconstructed)
|
|
182
|
+
return CombinedInterframeCodecContext(contexts=contexts)
|
|
183
|
+
|
|
184
|
+
def encode_keyframe(
|
|
185
|
+
self, context: CombinedInterframeCodecContext
|
|
186
|
+
) -> CombinedPayload:
|
|
187
|
+
"""
|
|
188
|
+
Encode the first frame as a keyframe.
|
|
189
|
+
|
|
190
|
+
Calls encode_keyframe on all sub-codecs and combines their payloads.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
context: The context of the first frame containing individual contexts for each sub-codec.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
A CombinedPayload containing all sub-codec payloads.
|
|
197
|
+
"""
|
|
198
|
+
# Call encode_keyframe on each sub-codec
|
|
199
|
+
payloads = []
|
|
200
|
+
for interface, context in zip(self.interfaces, context.contexts):
|
|
201
|
+
payload = interface.encode_keyframe(context)
|
|
202
|
+
payloads.append(payload)
|
|
203
|
+
return CombinedPayload(payloads=payloads)
|
|
204
|
+
|
|
205
|
+
def keyframe_to_context(
|
|
206
|
+
self, frame: GaussianModel, init_config: CombinedInterframeEncoderInitConfig
|
|
207
|
+
) -> CombinedInterframeCodecContext:
|
|
208
|
+
"""
|
|
209
|
+
Convert a keyframe to a Context.
|
|
210
|
+
|
|
211
|
+
Calls keyframe_to_context on all sub-codecs and combines their contexts.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
frame: The GaussianModel frame to convert.
|
|
215
|
+
init_config: Encoder initialization configuration containing individual configs for each sub-codec.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
A CombinedInterframeCodecContext containing all sub-codec contexts.
|
|
219
|
+
"""
|
|
220
|
+
# Call keyframe_to_context on each sub-codec
|
|
221
|
+
contexts = []
|
|
222
|
+
for interface, init_config in zip(self.interfaces, init_config.init_configs):
|
|
223
|
+
context = interface.keyframe_to_context(frame, init_config)
|
|
224
|
+
contexts.append(context)
|
|
225
|
+
return CombinedInterframeCodecContext(contexts=contexts)
|
|
226
|
+
|
|
227
|
+
def interframe_to_context(
|
|
228
|
+
self,
|
|
229
|
+
frame: GaussianModel,
|
|
230
|
+
prev_context: CombinedInterframeCodecContext,
|
|
231
|
+
) -> CombinedInterframeCodecContext:
|
|
232
|
+
"""
|
|
233
|
+
Convert a frame to a Context using the previous context as reference.
|
|
234
|
+
|
|
235
|
+
Calls interframe_to_context on all sub-codecs and combines their contexts.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
frame: The GaussianModel frame to convert.
|
|
239
|
+
prev_context: The context from the previous frame containing individual contexts for each sub-codec.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
A CombinedInterframeCodecContext containing all sub-codec contexts.
|
|
243
|
+
"""
|
|
244
|
+
# Call interframe_to_context on each sub-codec
|
|
245
|
+
contexts = []
|
|
246
|
+
for interface, prev_context in zip(self.interfaces, prev_context.contexts):
|
|
247
|
+
context = interface.interframe_to_context(frame, prev_context)
|
|
248
|
+
contexts.append(context)
|
|
249
|
+
return CombinedInterframeCodecContext(contexts=contexts)
|
|
250
|
+
|
|
251
|
+
def context_to_frame(
|
|
252
|
+
self, context: CombinedInterframeCodecContext, frame: GaussianModel
|
|
253
|
+
) -> GaussianModel:
|
|
254
|
+
"""
|
|
255
|
+
Convert a Context back to a frame.
|
|
256
|
+
|
|
257
|
+
Calls context_to_frame on all sub-codecs sequentially, applying each
|
|
258
|
+
context to the frame in order.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
context: The Context to convert containing individual contexts for each sub-codec.
|
|
262
|
+
frame: An empty GaussianModel or one from previous pipeline steps.
|
|
263
|
+
This frame will be modified in-place by each sub-codec.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
The modified GaussianModel with the frame data.
|
|
267
|
+
"""
|
|
268
|
+
# Apply each context sequentially to the frame
|
|
269
|
+
for interface, context in zip(self.interfaces, context.contexts):
|
|
270
|
+
frame = interface.context_to_frame(context, frame)
|
|
271
|
+
return frame
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from typing import Iterator, Optional
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
|
|
5
|
+
from gaussian_splatting import GaussianModel
|
|
6
|
+
|
|
7
|
+
from ..decoder import AbstractDecoder
|
|
8
|
+
from ..deserializer import AbstractDeserializer
|
|
9
|
+
from ..payload import Payload
|
|
10
|
+
from .interface import InterframeCodecContext, InterframeCodecInterface
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class InterframeDecoder(AbstractDecoder):
|
|
14
|
+
"""
|
|
15
|
+
Decoder that uses inter-frame decompression.
|
|
16
|
+
|
|
17
|
+
This decoder maintains an internal Context state and decodes frames
|
|
18
|
+
by applying deltas to the previous frame's context. The first frame
|
|
19
|
+
is decoded as a keyframe, and subsequent frames are decoded as deltas.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
deserializer: AbstractDeserializer,
|
|
25
|
+
interface: InterframeCodecInterface,
|
|
26
|
+
payload_device: str | torch.device | None = None,
|
|
27
|
+
device: str | torch.device | None = None,
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Initialize the inter-frame decoder.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
deserializer: The deserializer to use for converting bytes to Payload.
|
|
34
|
+
interface: The InterframeCodecInterface instance that provides decoding methods.
|
|
35
|
+
payload_device: The target device for input Payloads before
|
|
36
|
+
unpacking (e.g., 'cpu', 'cuda'). If None, no device
|
|
37
|
+
transfer is performed.
|
|
38
|
+
device: The target device for output GaussianModel frames
|
|
39
|
+
(e.g., 'cpu', 'cuda'). If None, no device transfer is performed.
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(deserializer, payload_device, device)
|
|
42
|
+
self._interface = interface
|
|
43
|
+
self._prev_context: Optional[InterframeCodecContext] = None
|
|
44
|
+
|
|
45
|
+
def create_empty_frame(self) -> GaussianModel:
|
|
46
|
+
"""
|
|
47
|
+
Create an empty GaussianModel for frame reconstruction.
|
|
48
|
+
|
|
49
|
+
This method is called before `context_to_frame` to provide a frame
|
|
50
|
+
that will be populated with data. Override this method if your
|
|
51
|
+
GaussianModel contains custom data or attributes that are not part of
|
|
52
|
+
the standard GaussianModel(), such as additional fields or custom
|
|
53
|
+
initialization logic.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
An empty GaussianModel instance.
|
|
57
|
+
"""
|
|
58
|
+
return GaussianModel(sh_degree=3)
|
|
59
|
+
|
|
60
|
+
def unpack(self, payload: Payload) -> Iterator[GaussianModel]:
|
|
61
|
+
"""
|
|
62
|
+
Unpack frame(s) from a Payload using inter-frame decoding.
|
|
63
|
+
|
|
64
|
+
The first payload is decoded as a keyframe. Subsequent payloads
|
|
65
|
+
are decoded as deltas from the previous frame.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
payload: A Payload instance to unpack.
|
|
69
|
+
|
|
70
|
+
Yields:
|
|
71
|
+
Unpacked GaussianModel instances.
|
|
72
|
+
"""
|
|
73
|
+
if self._prev_context is None:
|
|
74
|
+
# First frame: decode as keyframe
|
|
75
|
+
current_context = self._interface.decode_keyframe(payload)
|
|
76
|
+
else:
|
|
77
|
+
# Subsequent frames: decode as delta from previous
|
|
78
|
+
current_context = self._interface.decode_interframe(
|
|
79
|
+
payload, self._prev_context
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Update the previous context for next frame
|
|
83
|
+
self._prev_context = current_context
|
|
84
|
+
|
|
85
|
+
# Convert context back to frame
|
|
86
|
+
frame = self.create_empty_frame()
|
|
87
|
+
yield self._interface.context_to_frame(current_context, frame)
|
|
88
|
+
|
|
89
|
+
def flush_unpack(self) -> Iterator[GaussianModel]:
|
|
90
|
+
"""
|
|
91
|
+
Flush any remaining buffered frames from the unpacking stage.
|
|
92
|
+
|
|
93
|
+
For inter-frame decoding, there are no buffered frames to flush.
|
|
94
|
+
|
|
95
|
+
Yields:
|
|
96
|
+
No frames (empty iterator).
|
|
97
|
+
"""
|
|
98
|
+
return
|
|
99
|
+
yield # Make this a generator
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from typing import Iterator, Optional
|
|
2
|
+
|
|
3
|
+
from gaussian_splatting import GaussianModel
|
|
4
|
+
|
|
5
|
+
from ..encoder import AbstractEncoder
|
|
6
|
+
from ..payload import Payload
|
|
7
|
+
from ..serializer import AbstractSerializer
|
|
8
|
+
from .interface import InterframeCodecContext, InterframeEncoderInitConfig, InterframeCodecInterface
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class InterframeEncoder(AbstractEncoder):
|
|
12
|
+
"""
|
|
13
|
+
Encoder that uses inter-frame compression.
|
|
14
|
+
|
|
15
|
+
This encoder maintains an internal Context state and encodes frames
|
|
16
|
+
as differences from the previous frame. The first frame is encoded
|
|
17
|
+
as a keyframe, and subsequent frames are encoded as deltas.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
serializer: AbstractSerializer,
|
|
23
|
+
interface: InterframeCodecInterface,
|
|
24
|
+
init_config: InterframeEncoderInitConfig,
|
|
25
|
+
payload_device=None,
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Initialize the inter-frame encoder.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
serializer: The serializer to use for converting Payload to bytes.
|
|
32
|
+
interface: The InterframeCodecInterface instance that provides encoding methods.
|
|
33
|
+
init_config: Configuration parameters for keyframe initialization.
|
|
34
|
+
payload_device: The target device for encoded Payloads before
|
|
35
|
+
serialization (e.g., 'cpu', 'cuda'). If None, no device
|
|
36
|
+
transfer is performed.
|
|
37
|
+
"""
|
|
38
|
+
super().__init__(serializer, payload_device)
|
|
39
|
+
self._interface = interface
|
|
40
|
+
self._init_config = init_config
|
|
41
|
+
self._prev_context: Optional[InterframeCodecContext] = None
|
|
42
|
+
|
|
43
|
+
def pack(self, frame: GaussianModel) -> Iterator[Payload]:
|
|
44
|
+
"""
|
|
45
|
+
Pack a single frame into Payload objects using inter-frame encoding.
|
|
46
|
+
|
|
47
|
+
The first frame is encoded as a keyframe. Subsequent frames are
|
|
48
|
+
encoded as deltas from the previous frame.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
frame: A GaussianModel instance to pack.
|
|
52
|
+
|
|
53
|
+
Yields:
|
|
54
|
+
Packed Payload instances.
|
|
55
|
+
"""
|
|
56
|
+
if self._prev_context is None:
|
|
57
|
+
# First frame: convert and encode as keyframe
|
|
58
|
+
current_context = self._interface.keyframe_to_context(frame, self._init_config)
|
|
59
|
+
payload = self._interface.encode_keyframe(current_context)
|
|
60
|
+
# Decode back to get reconstructed context (avoid error accumulation)
|
|
61
|
+
reconstructed_context = self._interface.decode_keyframe_for_encode(
|
|
62
|
+
payload, current_context
|
|
63
|
+
)
|
|
64
|
+
else:
|
|
65
|
+
# Subsequent frames: convert and encode as delta from previous
|
|
66
|
+
current_context = self._interface.interframe_to_context(
|
|
67
|
+
frame, self._prev_context
|
|
68
|
+
)
|
|
69
|
+
payload = self._interface.encode_interframe(
|
|
70
|
+
self._prev_context, current_context
|
|
71
|
+
)
|
|
72
|
+
# Decode back to get reconstructed context (avoid error accumulation)
|
|
73
|
+
reconstructed_context = self._interface.decode_interframe_for_encode(
|
|
74
|
+
payload, self._prev_context
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Use reconstructed context as previous for next frame
|
|
78
|
+
self._prev_context = reconstructed_context
|
|
79
|
+
|
|
80
|
+
yield payload
|
|
81
|
+
|
|
82
|
+
def flush_pack(self) -> Iterator[Payload]:
|
|
83
|
+
"""
|
|
84
|
+
Flush any remaining buffered payloads from the packing stage.
|
|
85
|
+
|
|
86
|
+
For inter-frame encoding, there are no buffered payloads to flush.
|
|
87
|
+
|
|
88
|
+
Yields:
|
|
89
|
+
No payloads (empty iterator).
|
|
90
|
+
"""
|
|
91
|
+
return
|
|
92
|
+
yield # Make this a generator
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from gaussian_splatting import GaussianModel
|
|
5
|
+
|
|
6
|
+
from ..payload import Payload
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class InterframeEncoderInitConfig:
|
|
11
|
+
"""
|
|
12
|
+
Configuration for initializing the encoder when encoding the first frame (keyframe).
|
|
13
|
+
|
|
14
|
+
This dataclass is passed to `keyframe_to_context()` when the encoder processes
|
|
15
|
+
the first frame. It contains encoder-specific settings that determine how the
|
|
16
|
+
encoding context is initialized, such as quality settings, compression levels,
|
|
17
|
+
or algorithm parameters.
|
|
18
|
+
|
|
19
|
+
Subclasses should define specific fields for their encoding scheme.
|
|
20
|
+
|
|
21
|
+
Note:
|
|
22
|
+
This config is only used by the encoder during keyframe processing.
|
|
23
|
+
Configurations shared by both encoder and decoder should be stored
|
|
24
|
+
in InterframeCodecInterface.
|
|
25
|
+
"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class InterframeCodecContext:
|
|
31
|
+
"""
|
|
32
|
+
Context data for inter-frame encoding/decoding.
|
|
33
|
+
|
|
34
|
+
This dataclass holds the state information needed to encode/decode
|
|
35
|
+
frames relative to a reference frame. Both encoder and decoder
|
|
36
|
+
maintain their own InterframeCodecContext instances.
|
|
37
|
+
|
|
38
|
+
Subclasses should define specific fields for their encoding scheme.
|
|
39
|
+
|
|
40
|
+
Note:
|
|
41
|
+
This is for encoder/decoder runtime state (e.g., reference frame data,
|
|
42
|
+
accumulated statistics). Encoder initialization config should be stored
|
|
43
|
+
in InterframeEncoderInitConfig. Shared configurations should be stored
|
|
44
|
+
in InterframeCodecInterface.
|
|
45
|
+
"""
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InterframeCodecInterface(ABC):
|
|
50
|
+
"""
|
|
51
|
+
Abstract interface for inter-frame encoding/decoding algorithms.
|
|
52
|
+
|
|
53
|
+
This interface defines the methods required to implement inter-frame
|
|
54
|
+
compression, where frames are encoded as differences from previous frames.
|
|
55
|
+
|
|
56
|
+
Design Guidelines:
|
|
57
|
+
- InterframeCodecInterface: Store configurations shared by both encoder
|
|
58
|
+
and decoder (e.g., algorithm parameters that affect both encoding and
|
|
59
|
+
decoding, such as quantization tables or codebook sizes).
|
|
60
|
+
- InterframeCodecContext: Store encoder/decoder runtime state (e.g.,
|
|
61
|
+
reference frame data, accumulated statistics). Each side maintains
|
|
62
|
+
its own context instance.
|
|
63
|
+
- InterframeEncoderInitConfig: Store encoder initialization config that
|
|
64
|
+
is passed to `keyframe_to_context()` when encoding the first frame
|
|
65
|
+
(e.g., quality settings, compression levels).
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
@abstractmethod
|
|
69
|
+
def decode_interframe(self, payload: Payload, prev_context: InterframeCodecContext) -> InterframeCodecContext:
|
|
70
|
+
"""
|
|
71
|
+
Decode a delta payload to reconstruct the next frame's context.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
payload: The delta payload containing the difference data.
|
|
75
|
+
prev_context: The context of the previous frame.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
The reconstructed context for the current frame.
|
|
79
|
+
"""
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
@abstractmethod
|
|
83
|
+
def encode_interframe(self, prev_context: InterframeCodecContext, next_context: InterframeCodecContext) -> Payload:
|
|
84
|
+
"""
|
|
85
|
+
Encode the difference between two consecutive frames.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
prev_context: The context of the previous frame.
|
|
89
|
+
next_context: The context of the next frame.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
A payload containing the delta information.
|
|
93
|
+
"""
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
@abstractmethod
|
|
97
|
+
def decode_keyframe(self, payload: Payload) -> InterframeCodecContext:
|
|
98
|
+
"""
|
|
99
|
+
Decode a keyframe payload to create initial context.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
payload: The keyframe payload containing full frame data.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
The context for the first/key frame.
|
|
106
|
+
"""
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@abstractmethod
|
|
110
|
+
def decode_keyframe_for_encode(self, payload: Payload, context: InterframeCodecContext) -> InterframeCodecContext:
|
|
111
|
+
"""
|
|
112
|
+
Decode a keyframe payload during encoding to avoid error accumulation.
|
|
113
|
+
|
|
114
|
+
This method is used by the encoder after encoding a keyframe. It decodes
|
|
115
|
+
the payload back to get the reconstructed context that the decoder would
|
|
116
|
+
produce, which should be used as the reference for encoding subsequent
|
|
117
|
+
frames instead of the original context.
|
|
118
|
+
|
|
119
|
+
Unlike `decode_keyframe()`, this method may reuse data from the original
|
|
120
|
+
encoding context (passed as `context`) for efficiency, since certain
|
|
121
|
+
information may not need to be re-decoded during encoding.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
payload: The keyframe payload that was just encoded.
|
|
125
|
+
context: The original context used for encoding this keyframe.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
The reconstructed context as the decoder would produce it.
|
|
129
|
+
"""
|
|
130
|
+
pass
|
|
131
|
+
|
|
132
|
+
def decode_interframe_for_encode(
|
|
133
|
+
self, payload: Payload, prev_context: InterframeCodecContext
|
|
134
|
+
) -> InterframeCodecContext:
|
|
135
|
+
"""
|
|
136
|
+
Decode an interframe payload during encoding to avoid error accumulation.
|
|
137
|
+
|
|
138
|
+
This method is used by the encoder after encoding an interframe. It decodes
|
|
139
|
+
the payload back to get the reconstructed context that the decoder would
|
|
140
|
+
produce, which should be used as the reference for encoding subsequent
|
|
141
|
+
frames instead of the original context.
|
|
142
|
+
|
|
143
|
+
Unlike `decode_interframe()`, this method may reuse data from the previous
|
|
144
|
+
context for efficiency, since certain information (like codec parameters)
|
|
145
|
+
can be obtained from the context without re-decoding from the payload.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
payload: The interframe payload that was just encoded.
|
|
149
|
+
prev_context: The previous frame's context (reconstructed version).
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
The reconstructed context as the decoder would produce it.
|
|
153
|
+
"""
|
|
154
|
+
return self.decode_interframe(payload, prev_context)
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def encode_keyframe(self, context: InterframeCodecContext) -> Payload:
|
|
158
|
+
"""
|
|
159
|
+
Encode the first frame as a keyframe.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
context: The context of the first frame.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
A payload containing the full keyframe data.
|
|
166
|
+
"""
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
@abstractmethod
|
|
170
|
+
def keyframe_to_context(self, frame: GaussianModel, init_config: InterframeEncoderInitConfig) -> InterframeCodecContext:
|
|
171
|
+
"""
|
|
172
|
+
Convert a keyframe to a Context.
|
|
173
|
+
|
|
174
|
+
This method is called by the encoder when processing the first frame.
|
|
175
|
+
The init_config provides encoder-specific settings for initializing
|
|
176
|
+
the encoding context.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
frame: The GaussianModel frame to convert.
|
|
180
|
+
init_config: Encoder initialization configuration.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
The corresponding Context representation.
|
|
184
|
+
"""
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
@abstractmethod
|
|
188
|
+
def interframe_to_context(
|
|
189
|
+
self,
|
|
190
|
+
frame: GaussianModel,
|
|
191
|
+
prev_context: InterframeCodecContext,
|
|
192
|
+
) -> InterframeCodecContext:
|
|
193
|
+
"""
|
|
194
|
+
Convert a frame to a Context using the previous context as reference.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
frame: The GaussianModel frame to convert.
|
|
198
|
+
prev_context: The context from the previous frame.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
The corresponding Context representation.
|
|
202
|
+
"""
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
@abstractmethod
|
|
206
|
+
def context_to_frame(self, context: InterframeCodecContext, frame: GaussianModel) -> GaussianModel:
|
|
207
|
+
"""
|
|
208
|
+
Convert a Context back to a frame.
|
|
209
|
+
|
|
210
|
+
This method populates the provided GaussianModel with data from the
|
|
211
|
+
context and returns it.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
context: The Context to convert.
|
|
215
|
+
frame: An empty GaussianModel or one from previous pipeline steps.
|
|
216
|
+
This frame will be modified in-place with the context data.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
The modified GaussianModel with the frame data.
|
|
220
|
+
"""
|
|
221
|
+
pass
|