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,13 @@
|
|
|
1
|
+
from .decoder import AbstractDecoder
|
|
2
|
+
from .deserializer import AbstractDeserializer
|
|
3
|
+
from .encoder import AbstractEncoder
|
|
4
|
+
from .payload import Payload
|
|
5
|
+
from .serializer import AbstractSerializer
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"Payload",
|
|
9
|
+
"AbstractEncoder",
|
|
10
|
+
"AbstractDecoder",
|
|
11
|
+
"AbstractSerializer",
|
|
12
|
+
"AbstractDeserializer",
|
|
13
|
+
]
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command-line interface for gsvvcompressor.
|
|
3
|
+
|
|
4
|
+
This module provides encode and decode subcommands for each registered
|
|
5
|
+
encoder/decoder combination.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python -m gsvvcompressor encode <codec_name> [options]
|
|
9
|
+
python -m gsvvcompressor decode <codec_name> [options]
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
python -m gsvvcompressor encode vqxyzzstd \
|
|
13
|
+
input.first_frame_path=data/frame_0000.ply \
|
|
14
|
+
input.subsequent_format="data/frame_{:04d}.ply" \
|
|
15
|
+
output.path=compressed.bin
|
|
16
|
+
|
|
17
|
+
python -m gsvvcompressor decode vqxyzzstd \
|
|
18
|
+
input.path=compressed.bin \
|
|
19
|
+
output.first_frame_path=decoded/frame_0000.ply \
|
|
20
|
+
output.subsequent_format="decoded/frame_{:04d}.ply"
|
|
21
|
+
|
|
22
|
+
Hydra options:
|
|
23
|
+
--help Show configuration schema
|
|
24
|
+
--cfg job Show resolved configuration
|
|
25
|
+
--info config Show config search path
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import logging
|
|
29
|
+
import sys
|
|
30
|
+
from dataclasses import field, make_dataclass
|
|
31
|
+
from typing import Iterable, Iterator, List, Type, TypeVar
|
|
32
|
+
|
|
33
|
+
import hydra
|
|
34
|
+
from hydra.core.config_store import ConfigStore
|
|
35
|
+
from hydra.core.global_hydra import GlobalHydra
|
|
36
|
+
from omegaconf import DictConfig
|
|
37
|
+
|
|
38
|
+
from .combinations import ENCODERS, DECODERS
|
|
39
|
+
from .io import (
|
|
40
|
+
FrameReaderConfig,
|
|
41
|
+
FrameWriterConfig,
|
|
42
|
+
BytesReaderConfig,
|
|
43
|
+
BytesWriterConfig,
|
|
44
|
+
build_frame_reader,
|
|
45
|
+
build_frame_writer,
|
|
46
|
+
build_bytes_reader,
|
|
47
|
+
build_bytes_writer,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
logger = logging.getLogger(__name__)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# =============================================================================
|
|
54
|
+
# Progress Logging Utilities
|
|
55
|
+
# =============================================================================
|
|
56
|
+
|
|
57
|
+
def iter_with_progress(iterable: Iterator, desc: str) -> Iterator:
|
|
58
|
+
"""Wrap an iterable to log progress for each item."""
|
|
59
|
+
for i, item in enumerate(iterable):
|
|
60
|
+
logger.info(f"{desc} {i}")
|
|
61
|
+
yield item
|
|
62
|
+
logger.info(f"{desc} finished")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def format_size(size: int) -> str:
|
|
66
|
+
"""Format byte size to human-readable string (KB, MB, GB, etc.)."""
|
|
67
|
+
for unit in ("B", "KB", "MB", "GB", "TB"):
|
|
68
|
+
if abs(size) < 1024:
|
|
69
|
+
return f"{size:.2f} {unit}"
|
|
70
|
+
size /= 1024
|
|
71
|
+
return f"{size:.2f} PB"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def iter_with_size_logging(iterable: Iterator[bytes], desc: str) -> Iterator[bytes]:
|
|
75
|
+
"""Wrap a bytes iterable to log the size of each item."""
|
|
76
|
+
total_size = 0
|
|
77
|
+
for i, item in enumerate(iterable):
|
|
78
|
+
size = len(item)
|
|
79
|
+
total_size += size
|
|
80
|
+
logger.info(f"{desc} {i}: {format_size(size)} (total: {format_size(total_size)})")
|
|
81
|
+
yield item
|
|
82
|
+
logger.info(f"{desc} finished, total size: {format_size(total_size)}")
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# =============================================================================
|
|
86
|
+
# Dynamic Config Generation
|
|
87
|
+
# =============================================================================
|
|
88
|
+
|
|
89
|
+
def make_encode_config(codec_config_cls: Type) -> Type:
|
|
90
|
+
"""Dynamically create an encode config dataclass for a given codec."""
|
|
91
|
+
return make_dataclass(
|
|
92
|
+
f"Encode{codec_config_cls.__name__}",
|
|
93
|
+
[
|
|
94
|
+
("input", FrameReaderConfig, field(default_factory=FrameReaderConfig)),
|
|
95
|
+
("output", BytesWriterConfig, field(default_factory=BytesWriterConfig)),
|
|
96
|
+
("codec", codec_config_cls, field(default_factory=codec_config_cls)),
|
|
97
|
+
],
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def make_decode_config(codec_config_cls: Type) -> Type:
|
|
102
|
+
"""Dynamically create a decode config dataclass for a given codec."""
|
|
103
|
+
return make_dataclass(
|
|
104
|
+
f"Decode{codec_config_cls.__name__}",
|
|
105
|
+
[
|
|
106
|
+
("input", BytesReaderConfig, field(default_factory=BytesReaderConfig)),
|
|
107
|
+
("output", FrameWriterConfig, field(default_factory=FrameWriterConfig)),
|
|
108
|
+
("codec", codec_config_cls, field(default_factory=codec_config_cls)),
|
|
109
|
+
],
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# =============================================================================
|
|
114
|
+
# Encode/Decode Functions
|
|
115
|
+
# =============================================================================
|
|
116
|
+
|
|
117
|
+
def do_encode(cfg: DictConfig, codec_name: str) -> None:
|
|
118
|
+
"""Execute the encoding process with resolved config."""
|
|
119
|
+
encoder_entry = ENCODERS[codec_name]
|
|
120
|
+
|
|
121
|
+
# Build components from config
|
|
122
|
+
frame_reader = build_frame_reader(cfg.input)
|
|
123
|
+
bytes_writer = build_bytes_writer(cfg.output)
|
|
124
|
+
|
|
125
|
+
# Build encoder from codec config
|
|
126
|
+
encoder = encoder_entry.factory(cfg.codec)
|
|
127
|
+
|
|
128
|
+
logger.info(f"Encoding with {codec_name}...")
|
|
129
|
+
logger.info(f" Input: {cfg.input.first_frame_path}")
|
|
130
|
+
logger.info(f" Output: {cfg.output.path}")
|
|
131
|
+
|
|
132
|
+
# Read frames and encode
|
|
133
|
+
frame_stream = iter_with_progress(frame_reader.read(), "Reading frame")
|
|
134
|
+
encoded_stream = iter_with_progress(encoder.encode_stream(frame_stream), "Encoded chunk")
|
|
135
|
+
|
|
136
|
+
# Write encoded bytes with size logging
|
|
137
|
+
bytes_writer.write(iter_with_size_logging(encoded_stream, "Writing chunk"))
|
|
138
|
+
|
|
139
|
+
logger.info("Encoding complete!")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def do_decode(cfg: DictConfig, codec_name: str) -> None:
|
|
143
|
+
"""Execute the decoding process with resolved config."""
|
|
144
|
+
decoder_entry = DECODERS[codec_name]
|
|
145
|
+
|
|
146
|
+
# Build components from config
|
|
147
|
+
bytes_reader = build_bytes_reader(cfg.input)
|
|
148
|
+
frame_writer = build_frame_writer(cfg.output)
|
|
149
|
+
|
|
150
|
+
# Build decoder from codec config
|
|
151
|
+
decoder = decoder_entry.factory(cfg.codec)
|
|
152
|
+
|
|
153
|
+
logger.info(f"Decoding with {codec_name}...")
|
|
154
|
+
logger.info(f" Input: {cfg.input.path}")
|
|
155
|
+
logger.info(f" Output: {cfg.output.first_frame_path}")
|
|
156
|
+
|
|
157
|
+
# Read bytes and decode
|
|
158
|
+
bytes_stream = iter_with_size_logging(bytes_reader.read(), "Reading chunk")
|
|
159
|
+
decoded_stream = iter_with_progress(decoder.decode_stream(bytes_stream), "Decoded frame")
|
|
160
|
+
|
|
161
|
+
# Write decoded frames
|
|
162
|
+
frame_writer.write(iter_with_progress(decoded_stream, "Writing frame"))
|
|
163
|
+
|
|
164
|
+
logger.info("Decoding complete!")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def run_encode(codec_name: str, hydra_args: List[str]) -> None:
|
|
168
|
+
"""Run encoding with Hydra configuration."""
|
|
169
|
+
encoder_entry = ENCODERS[codec_name]
|
|
170
|
+
config_cls = make_encode_config(encoder_entry.config_class)
|
|
171
|
+
|
|
172
|
+
# Clear and register config
|
|
173
|
+
GlobalHydra.instance().clear()
|
|
174
|
+
cs = ConfigStore.instance()
|
|
175
|
+
cs.store(name="config", node=config_cls)
|
|
176
|
+
|
|
177
|
+
@hydra.main(version_base=None, config_path=None, config_name="config")
|
|
178
|
+
def _main(cfg: DictConfig) -> None:
|
|
179
|
+
do_encode(cfg, codec_name)
|
|
180
|
+
|
|
181
|
+
sys.argv = [sys.argv[0]] + hydra_args
|
|
182
|
+
_main()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def run_decode(codec_name: str, hydra_args: List[str]) -> None:
|
|
186
|
+
"""Run decoding with Hydra configuration."""
|
|
187
|
+
decoder_entry = DECODERS[codec_name]
|
|
188
|
+
config_cls = make_decode_config(decoder_entry.config_class)
|
|
189
|
+
|
|
190
|
+
# Clear and register config
|
|
191
|
+
GlobalHydra.instance().clear()
|
|
192
|
+
cs = ConfigStore.instance()
|
|
193
|
+
cs.store(name="config", node=config_cls)
|
|
194
|
+
|
|
195
|
+
@hydra.main(version_base=None, config_path=None, config_name="config")
|
|
196
|
+
def _main(cfg: DictConfig) -> None:
|
|
197
|
+
do_decode(cfg, codec_name)
|
|
198
|
+
|
|
199
|
+
sys.argv = [sys.argv[0]] + hydra_args
|
|
200
|
+
_main()
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
# =============================================================================
|
|
204
|
+
# Main Entry Point
|
|
205
|
+
# =============================================================================
|
|
206
|
+
|
|
207
|
+
def main() -> None:
|
|
208
|
+
"""Main entry point for the CLI."""
|
|
209
|
+
logging.basicConfig(
|
|
210
|
+
level=logging.INFO,
|
|
211
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
if len(sys.argv) < 2 or sys.argv[1] in ("--help", "-h", "help"):
|
|
215
|
+
print("Usage: python -m gsvvcompressor <encode|decode> <codec> [options]")
|
|
216
|
+
print(f" Encoders: {list(ENCODERS.keys())}")
|
|
217
|
+
print(f" Decoders: {list(DECODERS.keys())}")
|
|
218
|
+
print("Use --help after codec name for configuration options.")
|
|
219
|
+
sys.exit(0 if len(sys.argv) >= 2 else 1)
|
|
220
|
+
|
|
221
|
+
command = sys.argv[1]
|
|
222
|
+
if command not in ("encode", "decode"):
|
|
223
|
+
sys.exit(f"Error: Unknown command '{command}'. Use 'encode' or 'decode'.")
|
|
224
|
+
|
|
225
|
+
if len(sys.argv) < 3:
|
|
226
|
+
sys.exit(f"Error: Please specify a codec for {command}. Available: {list(ENCODERS.keys() if command == 'encode' else DECODERS.keys())}")
|
|
227
|
+
|
|
228
|
+
codec_name = sys.argv[2]
|
|
229
|
+
registry = ENCODERS if command == "encode" else DECODERS
|
|
230
|
+
if codec_name not in registry:
|
|
231
|
+
sys.exit(f"Error: Unknown {command}r '{codec_name}'. Available: {list(registry.keys())}")
|
|
232
|
+
|
|
233
|
+
# Remaining args go to Hydra (--help, --cfg, overrides, etc.)
|
|
234
|
+
hydra_args = sys.argv[3:]
|
|
235
|
+
|
|
236
|
+
if command == "encode":
|
|
237
|
+
run_encode(codec_name, hydra_args)
|
|
238
|
+
else:
|
|
239
|
+
run_decode(codec_name, hydra_args)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if __name__ == "__main__":
|
|
243
|
+
main()
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Combination modules for composing multiple codec components.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .registry import (
|
|
6
|
+
ENCODERS,
|
|
7
|
+
DECODERS,
|
|
8
|
+
register_encoder,
|
|
9
|
+
register_decoder,
|
|
10
|
+
EncoderEntry,
|
|
11
|
+
DecoderEntry,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Import to trigger registration
|
|
15
|
+
from . import vq_xyz_zstd
|
|
16
|
+
from . import vq_xyz_1mask_zstd
|
|
17
|
+
from . import vq_xyz_draco
|
|
18
|
+
from . import vq_xyz_draco_2pass
|
|
19
|
+
|
|
20
|
+
from .vq_xyz_zstd import (
|
|
21
|
+
VQXYZZstdEncoderConfig,
|
|
22
|
+
VQXYZZstdDecoderConfig,
|
|
23
|
+
VQXYZZstdEncoder,
|
|
24
|
+
VQXYZZstdDecoder,
|
|
25
|
+
build_vqxyzzstd_encoder,
|
|
26
|
+
build_vqxyzzstd_decoder,
|
|
27
|
+
)
|
|
28
|
+
from .vq_xyz_1mask_zstd import (
|
|
29
|
+
VQXYZ1MaskZstdEncoder,
|
|
30
|
+
VQXYZ1MaskZstdDecoder,
|
|
31
|
+
build_vqxyz1maskzstd_encoder,
|
|
32
|
+
build_vqxyz1maskzstd_decoder,
|
|
33
|
+
)
|
|
34
|
+
from .vq_xyz_draco import (
|
|
35
|
+
VQXYZDracoEncoderConfig,
|
|
36
|
+
VQXYZDracoDecoderConfig,
|
|
37
|
+
VQXYZDracoEncoder,
|
|
38
|
+
VQXYZDracoDecoder,
|
|
39
|
+
build_vqxyzdraco_encoder,
|
|
40
|
+
build_vqxyzdraco_decoder,
|
|
41
|
+
)
|
|
42
|
+
from .vq_xyz_draco_2pass import (
|
|
43
|
+
VQXYZDraco2PassEncoderConfig,
|
|
44
|
+
VQXYZDraco2PassDecoderConfig,
|
|
45
|
+
VQXYZDraco2PassEncoder,
|
|
46
|
+
VQXYZDraco2PassDecoder,
|
|
47
|
+
build_vqxyzdraco2pass_encoder,
|
|
48
|
+
build_vqxyzdraco2pass_decoder,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"ENCODERS",
|
|
53
|
+
"DECODERS",
|
|
54
|
+
"register_encoder",
|
|
55
|
+
"register_decoder",
|
|
56
|
+
"EncoderEntry",
|
|
57
|
+
"DecoderEntry",
|
|
58
|
+
# VQ + XYZ + Zstd
|
|
59
|
+
"VQXYZZstdEncoderConfig",
|
|
60
|
+
"VQXYZZstdDecoderConfig",
|
|
61
|
+
"VQXYZZstdEncoder",
|
|
62
|
+
"VQXYZZstdDecoder",
|
|
63
|
+
"build_vqxyzzstd_encoder",
|
|
64
|
+
"build_vqxyzzstd_decoder",
|
|
65
|
+
# VQ (single merged mask) + XYZ + Zstd
|
|
66
|
+
"VQXYZ1MaskZstdEncoder",
|
|
67
|
+
"VQXYZ1MaskZstdDecoder",
|
|
68
|
+
"build_vqxyz1maskzstd_encoder",
|
|
69
|
+
"build_vqxyz1maskzstd_decoder",
|
|
70
|
+
# VQ + XYZ + Draco
|
|
71
|
+
"VQXYZDracoEncoderConfig",
|
|
72
|
+
"VQXYZDracoDecoderConfig",
|
|
73
|
+
"VQXYZDracoEncoder",
|
|
74
|
+
"VQXYZDracoDecoder",
|
|
75
|
+
"build_vqxyzdraco_encoder",
|
|
76
|
+
"build_vqxyzdraco_decoder",
|
|
77
|
+
# VQ + XYZ + Two-Pass Draco
|
|
78
|
+
"VQXYZDraco2PassEncoderConfig",
|
|
79
|
+
"VQXYZDraco2PassDecoderConfig",
|
|
80
|
+
"VQXYZDraco2PassEncoder",
|
|
81
|
+
"VQXYZDraco2PassDecoder",
|
|
82
|
+
"build_vqxyzdraco2pass_encoder",
|
|
83
|
+
"build_vqxyzdraco2pass_decoder",
|
|
84
|
+
]
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Global registry for encoder and decoder combinations.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Callable, Dict, Type
|
|
7
|
+
|
|
8
|
+
from ..encoder import AbstractEncoder
|
|
9
|
+
from ..decoder import AbstractDecoder
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class EncoderEntry:
|
|
14
|
+
"""Entry in the encoder registry."""
|
|
15
|
+
name: str
|
|
16
|
+
factory: Callable[..., AbstractEncoder]
|
|
17
|
+
config_class: Type
|
|
18
|
+
description: str = ""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class DecoderEntry:
|
|
23
|
+
"""Entry in the decoder registry."""
|
|
24
|
+
name: str
|
|
25
|
+
factory: Callable[..., AbstractDecoder]
|
|
26
|
+
config_class: Type
|
|
27
|
+
description: str = ""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Global registries
|
|
31
|
+
ENCODERS: Dict[str, EncoderEntry] = {}
|
|
32
|
+
DECODERS: Dict[str, DecoderEntry] = {}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def register_encoder(
|
|
36
|
+
name: str,
|
|
37
|
+
factory: Callable[..., AbstractEncoder],
|
|
38
|
+
config_class: Type,
|
|
39
|
+
description: str = "",
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Register an encoder."""
|
|
42
|
+
ENCODERS[name] = EncoderEntry(name, factory, config_class, description)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def register_decoder(
|
|
46
|
+
name: str,
|
|
47
|
+
factory: Callable[..., AbstractDecoder],
|
|
48
|
+
config_class: Type,
|
|
49
|
+
description: str = "",
|
|
50
|
+
) -> None:
|
|
51
|
+
"""Register a decoder."""
|
|
52
|
+
DECODERS[name] = DecoderEntry(name, factory, config_class, description)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VQ + XYZ quantization combined codec interface with merged mask.
|
|
3
|
+
|
|
4
|
+
This module provides the combined VQ + XYZ codec interface that uses a single
|
|
5
|
+
merged mask for interframe encoding.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from ..interframe.combine import (
|
|
9
|
+
CombinedInterframeCodecInterface,
|
|
10
|
+
CombinedInterframeCodecContext,
|
|
11
|
+
CombinedPayload,
|
|
12
|
+
)
|
|
13
|
+
from ..vq.interface import VQInterframeCodecContext
|
|
14
|
+
from ..vq.singlemask import VQMergeMaskInterframeCodecInterface, VQMergeMaskInterframePayload
|
|
15
|
+
from ..xyz.interface import (
|
|
16
|
+
XYZQuantInterframeCodecInterface,
|
|
17
|
+
XYZQuantInterframeCodecContext,
|
|
18
|
+
XYZQuantInterframePayload,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class VQXYZQuantMergeMaskInterframeCodecInterface(CombinedInterframeCodecInterface):
|
|
23
|
+
"""
|
|
24
|
+
Combined VQ + XYZ codec with single merged mask for interframe encoding.
|
|
25
|
+
|
|
26
|
+
This interface extends CombinedInterframeCodecInterface but uses a single merged mask
|
|
27
|
+
(OR of XYZ mask and all VQ attribute masks) for interframe payloads. Both XYZ and VQ
|
|
28
|
+
payloads use the same mask, improving compression efficiency.
|
|
29
|
+
|
|
30
|
+
Assumes interfaces order: [xyz_interface, vq_interface]
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the combined codec with XYZ and VQ interfaces.
|
|
36
|
+
"""
|
|
37
|
+
xyz_interface = XYZQuantInterframeCodecInterface()
|
|
38
|
+
vq_interface = VQMergeMaskInterframeCodecInterface()
|
|
39
|
+
super().__init__([xyz_interface, vq_interface])
|
|
40
|
+
self.xyz_interface = xyz_interface
|
|
41
|
+
self.vq_interface = vq_interface
|
|
42
|
+
|
|
43
|
+
def encode_interframe(
|
|
44
|
+
self,
|
|
45
|
+
prev_context: CombinedInterframeCodecContext,
|
|
46
|
+
next_context: CombinedInterframeCodecContext,
|
|
47
|
+
) -> CombinedPayload:
|
|
48
|
+
"""
|
|
49
|
+
Encode the difference between two consecutive frames.
|
|
50
|
+
|
|
51
|
+
Calls super to get individual payloads, then merges masks (OR) and
|
|
52
|
+
re-extracts data using the merged mask.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
prev_context: The context of the previous frame.
|
|
56
|
+
next_context: The context of the next frame.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
A CombinedPayload with XYZ and VQ payloads using the same merged mask.
|
|
60
|
+
"""
|
|
61
|
+
# Get original payloads from parent
|
|
62
|
+
original_payload: CombinedPayload = super().encode_interframe(prev_context, next_context)
|
|
63
|
+
xyz_original: XYZQuantInterframePayload = original_payload.payloads[0]
|
|
64
|
+
vq_original: VQMergeMaskInterframePayload = original_payload.payloads[1]
|
|
65
|
+
|
|
66
|
+
# Merge masks: XYZ mask OR VQ mask
|
|
67
|
+
merged_mask = xyz_original.xyz_mask | vq_original.ids_mask
|
|
68
|
+
|
|
69
|
+
# Get next context data
|
|
70
|
+
xyz_next_context: XYZQuantInterframeCodecContext = next_context.contexts[0]
|
|
71
|
+
vq_next_context: VQInterframeCodecContext = next_context.contexts[1]
|
|
72
|
+
|
|
73
|
+
# Create XYZ payload with merged mask
|
|
74
|
+
xyz_payload = XYZQuantInterframePayload(
|
|
75
|
+
xyz_mask=merged_mask,
|
|
76
|
+
quantized_xyz=xyz_next_context.quantized_xyz[merged_mask],
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Create VQ payload with merged mask
|
|
80
|
+
changed_ids_dict = {}
|
|
81
|
+
for key, next_ids in vq_next_context.ids_dict.items():
|
|
82
|
+
changed_ids_dict[key] = next_ids[merged_mask]
|
|
83
|
+
|
|
84
|
+
vq_payload = VQMergeMaskInterframePayload(
|
|
85
|
+
ids_mask=merged_mask,
|
|
86
|
+
ids_dict=changed_ids_dict,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return CombinedPayload(payloads=[xyz_payload, vq_payload])
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
VQ + XYZ quantization with single merged mask + Zstd compression encoder/decoder combination.
|
|
3
|
+
|
|
4
|
+
This module provides a combined codec where VQ and XYZ share a single merged mask
|
|
5
|
+
for interframe encoding, improving compression efficiency.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from ..compress.zstd import ZstdSerializer, ZstdDeserializer
|
|
12
|
+
from ..interframe.combine import CombinedInterframeEncoderInitConfig
|
|
13
|
+
from ..interframe.encoder import InterframeEncoder
|
|
14
|
+
from ..interframe.decoder import InterframeDecoder
|
|
15
|
+
from ..vq.interface import VQInterframeCodecConfig
|
|
16
|
+
from ..xyz.interface import XYZQuantInterframeCodecConfig
|
|
17
|
+
from .registry import register_encoder, register_decoder
|
|
18
|
+
from .vq_xyz_1mask import VQXYZQuantMergeMaskInterframeCodecInterface
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def VQXYZ1MaskZstdEncoder(
|
|
22
|
+
vq_config: VQInterframeCodecConfig,
|
|
23
|
+
xyz_config: XYZQuantInterframeCodecConfig,
|
|
24
|
+
zstd_level: int = 7,
|
|
25
|
+
payload_device: Optional[str] = None,
|
|
26
|
+
) -> InterframeEncoder:
|
|
27
|
+
"""Create an encoder combining VQ + XYZ quantization with merged mask + Zstd compression."""
|
|
28
|
+
combined_interface = VQXYZQuantMergeMaskInterframeCodecInterface()
|
|
29
|
+
combined_config = CombinedInterframeEncoderInitConfig(
|
|
30
|
+
init_configs=[xyz_config, vq_config]
|
|
31
|
+
)
|
|
32
|
+
serializer = ZstdSerializer(level=zstd_level)
|
|
33
|
+
return InterframeEncoder(
|
|
34
|
+
serializer=serializer,
|
|
35
|
+
interface=combined_interface,
|
|
36
|
+
init_config=combined_config,
|
|
37
|
+
payload_device=payload_device,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def VQXYZ1MaskZstdDecoder(
|
|
42
|
+
payload_device: Optional[str] = None,
|
|
43
|
+
device: Optional[str] = None,
|
|
44
|
+
) -> InterframeDecoder:
|
|
45
|
+
"""Create a decoder for VQ + XYZ quantization with merged mask + Zstd compressed data."""
|
|
46
|
+
combined_interface = VQXYZQuantMergeMaskInterframeCodecInterface()
|
|
47
|
+
deserializer = ZstdDeserializer()
|
|
48
|
+
return InterframeDecoder(
|
|
49
|
+
deserializer=deserializer,
|
|
50
|
+
interface=combined_interface,
|
|
51
|
+
payload_device=payload_device,
|
|
52
|
+
device=device,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class VQXYZ1MaskZstdEncoderConfig:
|
|
58
|
+
"""Configuration for VQ + XYZ (merged mask) + Zstd encoder."""
|
|
59
|
+
vq: VQInterframeCodecConfig = field(default_factory=VQInterframeCodecConfig)
|
|
60
|
+
xyz: XYZQuantInterframeCodecConfig = field(default_factory=XYZQuantInterframeCodecConfig)
|
|
61
|
+
zstd_level: int = 7
|
|
62
|
+
payload_device: Optional[str] = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class VQXYZ1MaskZstdDecoderConfig:
|
|
67
|
+
"""Configuration for VQ + XYZ (merged mask) + Zstd decoder."""
|
|
68
|
+
payload_device: Optional[str] = None
|
|
69
|
+
device: Optional[str] = None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def build_vqxyz1maskzstd_encoder(config: VQXYZ1MaskZstdEncoderConfig) -> InterframeEncoder:
|
|
73
|
+
"""Build encoder from configuration."""
|
|
74
|
+
return VQXYZ1MaskZstdEncoder(
|
|
75
|
+
vq_config=config.vq,
|
|
76
|
+
xyz_config=config.xyz,
|
|
77
|
+
zstd_level=config.zstd_level,
|
|
78
|
+
payload_device=config.payload_device,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def build_vqxyz1maskzstd_decoder(config: VQXYZ1MaskZstdDecoderConfig) -> InterframeDecoder:
|
|
83
|
+
"""Build decoder from configuration."""
|
|
84
|
+
return VQXYZ1MaskZstdDecoder(
|
|
85
|
+
payload_device=config.payload_device,
|
|
86
|
+
device=config.device,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# Register
|
|
91
|
+
register_encoder(
|
|
92
|
+
"vqxyz1maskzstd",
|
|
93
|
+
build_vqxyz1maskzstd_encoder,
|
|
94
|
+
VQXYZ1MaskZstdEncoderConfig,
|
|
95
|
+
"VQ + XYZ quantization (merged mask) + Zstd compression",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
register_decoder(
|
|
99
|
+
"vqxyz1maskzstd",
|
|
100
|
+
build_vqxyz1maskzstd_decoder,
|
|
101
|
+
VQXYZ1MaskZstdDecoderConfig,
|
|
102
|
+
"VQ + XYZ quantization (merged mask) + Zstd decompression",
|
|
103
|
+
)
|