splatthis 0.2.0__py3-none-any.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.
- splatthis/__init__.py +50 -0
- splatthis/_version.py +3 -0
- splatthis/adaptive_compute.py +520 -0
- splatthis/artifact_backends.py +556 -0
- splatthis/artifact_evaluation.py +126 -0
- splatthis/artifact_gates.py +368 -0
- splatthis/artifact_io.py +45 -0
- splatthis/browser_capture.py +815 -0
- splatthis/browser_export.py +426 -0
- splatthis/budgets.py +338 -0
- splatthis/canvas_parity.py +107 -0
- splatthis/cli.py +721 -0
- splatthis/color.py +50 -0
- splatthis/config.py +214 -0
- splatthis/conversion_engine.py +125 -0
- splatthis/converter.py +12 -0
- splatthis/distillation.py +608 -0
- splatthis/domain.py +82 -0
- splatthis/engine_artifacts.py +379 -0
- splatthis/engine_configuration.py +891 -0
- splatthis/engine_densification.py +769 -0
- splatthis/engine_guidance.py +702 -0
- splatthis/engine_initialization.py +511 -0
- splatthis/engine_optimization.py +857 -0
- splatthis/engine_postfit.py +887 -0
- splatthis/engine_state.py +285 -0
- splatthis/export_common.py +388 -0
- splatthis/features.py +492 -0
- splatthis/fidelity/__init__.py +32 -0
- splatthis/fidelity/analysis.py +101 -0
- splatthis/fidelity/config.py +66 -0
- splatthis/fidelity/evaluator.py +183 -0
- splatthis/fidelity/metrics.py +244 -0
- splatthis/fidelity/operators.py +97 -0
- splatthis/fidelity/report.py +43 -0
- splatthis/fidelity/stage.py +176 -0
- splatthis/io.py +132 -0
- splatthis/mixed_primitives.py +447 -0
- splatthis/mlx_losses.py +252 -0
- splatthis/mlx_optimizer.py +176 -0
- splatthis/mlx_renderer.py +537 -0
- splatthis/mlx_runtime.py +56 -0
- splatthis/mlx_stage.py +504 -0
- splatthis/optimizer.py +131 -0
- splatthis/pipeline.py +95 -0
- splatthis/pipeline_artifacts.py +335 -0
- splatthis/pipeline_phases.py +442 -0
- splatthis/pixel_runtime.py +987 -0
- splatthis/pptx_export.py +745 -0
- splatthis/profiles.py +365 -0
- splatthis/proxies.py +165 -0
- splatthis/py.typed +0 -0
- splatthis/quality.py +76 -0
- splatthis/renderer.py +1428 -0
- splatthis/reporting.py +72 -0
- splatthis/roundtrip.py +84 -0
- splatthis/splat.py +549 -0
- splatthis/storage.py +202 -0
- splatthis/svg_export.py +1106 -0
- splatthis/svg_recipe_gate.py +236 -0
- splatthis/template_assets.py +36 -0
- splatthis/templates/__init__.py +1 -0
- splatthis/templates/drawingml/background_shape.xml +31 -0
- splatthis/templates/drawingml/blur.xml +1 -0
- splatthis/templates/drawingml/close_group.xml +1 -0
- splatthis/templates/drawingml/edge_segment.xml +21 -0
- splatthis/templates/drawingml/effect_list.xml +3 -0
- splatthis/templates/drawingml/gradient_fill.xml +7 -0
- splatthis/templates/drawingml/gradient_stop.xml +3 -0
- splatthis/templates/drawingml/group.xml +14 -0
- splatthis/templates/drawingml/shape.xml +30 -0
- splatthis/templates/drawingml/shape_no_effect.xml +29 -0
- splatthis/templates/drawingml/slide.xml +24 -0
- splatthis/templates/drawingml/soft_edge.xml +1 -0
- splatthis/templates/drawingml/solid_fill.xml +3 -0
- splatthis/templates/pptx/app_props.xml +7 -0
- splatthis/templates/pptx/content_types.xml +15 -0
- splatthis/templates/pptx/core_props.xml +12 -0
- splatthis/templates/pptx/pres_props.xml +4 -0
- splatthis/templates/pptx/presentation.xml +14 -0
- splatthis/templates/pptx/presentation_rels.xml +8 -0
- splatthis/templates/pptx/raster_slide.xml +49 -0
- splatthis/templates/pptx/raster_slide_rels.xml +5 -0
- splatthis/templates/pptx/root_rels.xml +6 -0
- splatthis/templates/pptx/slide_layout.xml +26 -0
- splatthis/templates/pptx/slide_layout_rels.xml +4 -0
- splatthis/templates/pptx/slide_master.xml +39 -0
- splatthis/templates/pptx/slide_master_rels.xml +5 -0
- splatthis/templates/pptx/theme.xml +55 -0
- splatthis/templates/pptx/vector_slide_rels.xml +4 -0
- splatthis/templates/pptx/view_props.xml +4 -0
- splatthis/templates/reporting/side_by_side.html +45 -0
- splatthis/templates/svg/background.svg +1 -0
- splatthis/templates/svg/blur_document.svg +7 -0
- splatthis/templates/svg/blur_ellipse.svg +1 -0
- splatthis/templates/svg/blur_filter.svg +3 -0
- splatthis/templates/svg/close.svg +1 -0
- splatthis/templates/svg/edge_group.svg +2 -0
- splatthis/templates/svg/edge_line.svg +1 -0
- splatthis/templates/svg/edge_path.svg +1 -0
- splatthis/templates/svg/ellipse.svg +1 -0
- splatthis/templates/svg/empty_document.svg +3 -0
- splatthis/templates/svg/palette_background.svg +1 -0
- splatthis/templates/svg/palette_document.svg +9 -0
- splatthis/templates/svg/palette_ellipse.svg +1 -0
- splatthis/templates/svg/palette_gradient.svg +3 -0
- splatthis/templates/svg/palette_stop.svg +1 -0
- splatthis/templates/svg/scripted_document.svg +9 -0
- splatthis/templates/svg/scripted_runtime.js +52 -0
- splatthis/templates/svg/standard_document.svg +12 -0
- splatthis/templates/svg/standard_gradient.svg +3 -0
- splatthis/templates/svg/standard_stop.svg +1 -0
- splatthis-0.2.0.dist-info/METADATA +455 -0
- splatthis-0.2.0.dist-info/RECORD +118 -0
- splatthis-0.2.0.dist-info/WHEEL +5 -0
- splatthis-0.2.0.dist-info/entry_points.txt +2 -0
- splatthis-0.2.0.dist-info/licenses/LICENSE +21 -0
- splatthis-0.2.0.dist-info/top_level.txt +1 -0
splatthis/__init__.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Target-aware 2D Gaussian splat conversion and browser runtimes."""
|
|
2
|
+
|
|
3
|
+
from ._version import __version__
|
|
4
|
+
from .artifact_evaluation import (
|
|
5
|
+
evaluate_css_export_quality,
|
|
6
|
+
evaluate_native_canvas_export_quality,
|
|
7
|
+
evaluate_svg_export_quality,
|
|
8
|
+
)
|
|
9
|
+
from .browser_export import generate_css_splat_html, generate_native_canvas_html
|
|
10
|
+
from .config import ConversionRequest, ConverterConfig
|
|
11
|
+
from .converter import PNG2SVGConverter
|
|
12
|
+
from .domain import EvidenceLevel, SplatScene
|
|
13
|
+
from .pixel_runtime import (
|
|
14
|
+
generate_parallax_pixel_runtime_html,
|
|
15
|
+
generate_pixel_runtime_html,
|
|
16
|
+
generate_webgl_pixel_runtime_html,
|
|
17
|
+
)
|
|
18
|
+
from .pptx_export import save_pptx_with_splat_png, save_pptx_with_splats
|
|
19
|
+
from .reporting import save_side_by_side_html
|
|
20
|
+
from .roundtrip import validate_export_roundtrip
|
|
21
|
+
from .splat import GaussianSplat, RawSplat
|
|
22
|
+
from .storage import load_png, load_splats_json, render_splats_preview_png
|
|
23
|
+
from .svg_export import save_svg
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"__version__",
|
|
27
|
+
"GaussianSplat",
|
|
28
|
+
"RawSplat",
|
|
29
|
+
"SplatScene",
|
|
30
|
+
"EvidenceLevel",
|
|
31
|
+
"ConversionRequest",
|
|
32
|
+
"ConverterConfig",
|
|
33
|
+
"load_png",
|
|
34
|
+
"save_svg",
|
|
35
|
+
"save_pptx_with_splats",
|
|
36
|
+
"save_pptx_with_splat_png",
|
|
37
|
+
"load_splats_json",
|
|
38
|
+
"render_splats_preview_png",
|
|
39
|
+
"save_side_by_side_html",
|
|
40
|
+
"generate_css_splat_html",
|
|
41
|
+
"generate_native_canvas_html",
|
|
42
|
+
"generate_pixel_runtime_html",
|
|
43
|
+
"generate_webgl_pixel_runtime_html",
|
|
44
|
+
"generate_parallax_pixel_runtime_html",
|
|
45
|
+
"evaluate_css_export_quality",
|
|
46
|
+
"evaluate_native_canvas_export_quality",
|
|
47
|
+
"evaluate_svg_export_quality",
|
|
48
|
+
"validate_export_roundtrip",
|
|
49
|
+
"PNG2SVGConverter",
|
|
50
|
+
]
|
splatthis/_version.py
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
"""Canvas adaptive-compute policies.
|
|
2
|
+
|
|
3
|
+
The online controller stops only on an absolute target observed in the current
|
|
4
|
+
run. The retrospective helpers can measure whether broader stopping or scaling
|
|
5
|
+
rules would have saved work, but do not predict an unseen 4k or 8k result.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Any, Dict, Mapping, Optional, Sequence, Tuple
|
|
12
|
+
|
|
13
|
+
# Full-frame model-to-Chrome calibration, data/canvas-checkpoint-parity.json.
|
|
14
|
+
DEFAULT_CHROME_SSIM_SAFETY_MARGIN = 0.0
|
|
15
|
+
DEFAULT_CHROME_PSNR_SAFETY_MARGIN = 0.0
|
|
16
|
+
CANVAS_RUNTIME_SCORER = "canvas-image-data-byte-v1"
|
|
17
|
+
CANVAS_RUNTIME_CALIBRATION_CHECKPOINTS = 48
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class CanvasCheckpoint:
|
|
22
|
+
label: str
|
|
23
|
+
ssim_srgb: float
|
|
24
|
+
psnr_srgb: float
|
|
25
|
+
splat_count: int
|
|
26
|
+
elapsed_sec: float
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True)
|
|
30
|
+
class AdaptiveComputePolicy:
|
|
31
|
+
"""Policy parameters for replaying an observed checkpoint curve."""
|
|
32
|
+
|
|
33
|
+
min_checkpoints: int = 2
|
|
34
|
+
target_ssim_srgb: Optional[float] = 0.98
|
|
35
|
+
target_psnr_srgb: Optional[float] = None
|
|
36
|
+
checkpoint_min_ssim_gain: float = 0.0005
|
|
37
|
+
max_ssim_regression: float = 0.0005
|
|
38
|
+
max_psnr_regression: float = 0.10
|
|
39
|
+
plateau_min_ssim_gain: float = 0.002
|
|
40
|
+
plateau_min_psnr_gain: float = 0.10
|
|
41
|
+
min_ssim_gain_per_second: float = 0.0
|
|
42
|
+
stop_on_regression: bool = True
|
|
43
|
+
stop_on_plateau: bool = True
|
|
44
|
+
|
|
45
|
+
def __post_init__(self) -> None:
|
|
46
|
+
if self.min_checkpoints < 1:
|
|
47
|
+
raise ValueError("min_checkpoints must be positive")
|
|
48
|
+
for name in (
|
|
49
|
+
"checkpoint_min_ssim_gain",
|
|
50
|
+
"max_ssim_regression",
|
|
51
|
+
"max_psnr_regression",
|
|
52
|
+
"plateau_min_ssim_gain",
|
|
53
|
+
"plateau_min_psnr_gain",
|
|
54
|
+
"min_ssim_gain_per_second",
|
|
55
|
+
):
|
|
56
|
+
if float(getattr(self, name)) < 0.0:
|
|
57
|
+
raise ValueError(f"{name} must be non-negative")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass(frozen=True)
|
|
61
|
+
class OnlineAdaptiveConfig:
|
|
62
|
+
"""Conservative online policy that only stops on an absolute quality target.
|
|
63
|
+
|
|
64
|
+
Plateau, regression, and higher-budget prediction remain retrospective
|
|
65
|
+
experiments. The online controller sees only completed pixel-runtime checkpoints
|
|
66
|
+
and cannot use a future result to justify a stop.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
enabled: bool = False
|
|
70
|
+
min_checkpoints: int = 2
|
|
71
|
+
target_ssim_srgb: Optional[float] = 0.98
|
|
72
|
+
target_psnr_srgb: Optional[float] = None
|
|
73
|
+
chrome_ssim_safety_margin: float = DEFAULT_CHROME_SSIM_SAFETY_MARGIN
|
|
74
|
+
chrome_psnr_safety_margin: float = DEFAULT_CHROME_PSNR_SAFETY_MARGIN
|
|
75
|
+
checkpoint_min_ssim_gain: float = 0.0005
|
|
76
|
+
max_ssim_regression: float = 0.0005
|
|
77
|
+
max_psnr_regression: float = 0.10
|
|
78
|
+
|
|
79
|
+
def __post_init__(self) -> None:
|
|
80
|
+
if self.min_checkpoints < 1:
|
|
81
|
+
raise ValueError("adaptive_compute_min_checkpoints must be positive")
|
|
82
|
+
if self.target_ssim_srgb is not None and not (
|
|
83
|
+
0.0 <= self.target_ssim_srgb <= 1.0
|
|
84
|
+
):
|
|
85
|
+
raise ValueError(
|
|
86
|
+
"adaptive_compute_target_ssim_srgb must be between 0 and 1"
|
|
87
|
+
)
|
|
88
|
+
if self.target_psnr_srgb is not None and self.target_psnr_srgb < 0.0:
|
|
89
|
+
raise ValueError("adaptive_compute_target_psnr_srgb must be non-negative")
|
|
90
|
+
if self.chrome_ssim_safety_margin < 0.0:
|
|
91
|
+
raise ValueError("adaptive_compute_chrome_ssim_margin must be non-negative")
|
|
92
|
+
if self.chrome_psnr_safety_margin < 0.0:
|
|
93
|
+
raise ValueError("adaptive_compute_chrome_psnr_margin must be non-negative")
|
|
94
|
+
for name in (
|
|
95
|
+
"checkpoint_min_ssim_gain",
|
|
96
|
+
"max_ssim_regression",
|
|
97
|
+
"max_psnr_regression",
|
|
98
|
+
):
|
|
99
|
+
if float(getattr(self, name)) < 0.0:
|
|
100
|
+
raise ValueError(f"{name} must be non-negative")
|
|
101
|
+
if (
|
|
102
|
+
self.enabled
|
|
103
|
+
and self.target_ssim_srgb is None
|
|
104
|
+
and self.target_psnr_srgb is None
|
|
105
|
+
):
|
|
106
|
+
raise ValueError("adaptive compute requires an SSIM or PSNR quality target")
|
|
107
|
+
if (
|
|
108
|
+
self.enabled
|
|
109
|
+
and self.effective_model_ssim_threshold is not None
|
|
110
|
+
and self.effective_model_ssim_threshold > 1.0
|
|
111
|
+
):
|
|
112
|
+
raise ValueError(
|
|
113
|
+
"adaptive SSIM target plus Chrome safety margin must not exceed 1"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def effective_model_ssim_threshold(self) -> Optional[float]:
|
|
118
|
+
if self.target_ssim_srgb is None:
|
|
119
|
+
return None
|
|
120
|
+
return float(self.target_ssim_srgb + self.chrome_ssim_safety_margin)
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def effective_model_psnr_threshold(self) -> Optional[float]:
|
|
124
|
+
if self.target_psnr_srgb is None:
|
|
125
|
+
return None
|
|
126
|
+
return float(self.target_psnr_srgb + self.chrome_psnr_safety_margin)
|
|
127
|
+
|
|
128
|
+
def checkpoint_policy(self) -> AdaptiveComputePolicy:
|
|
129
|
+
"""Return the shared checkpoint-selection policy used by the converter."""
|
|
130
|
+
|
|
131
|
+
return AdaptiveComputePolicy(
|
|
132
|
+
min_checkpoints=self.min_checkpoints,
|
|
133
|
+
target_ssim_srgb=self.target_ssim_srgb,
|
|
134
|
+
target_psnr_srgb=self.target_psnr_srgb,
|
|
135
|
+
checkpoint_min_ssim_gain=self.checkpoint_min_ssim_gain,
|
|
136
|
+
max_ssim_regression=self.max_ssim_regression,
|
|
137
|
+
max_psnr_regression=self.max_psnr_regression,
|
|
138
|
+
plateau_min_ssim_gain=0.0,
|
|
139
|
+
plateau_min_psnr_gain=0.0,
|
|
140
|
+
min_ssim_gain_per_second=0.0,
|
|
141
|
+
stop_on_regression=False,
|
|
142
|
+
stop_on_plateau=False,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
146
|
+
return {
|
|
147
|
+
**self.__dict__,
|
|
148
|
+
"effective_model_ssim_threshold": self.effective_model_ssim_threshold,
|
|
149
|
+
"effective_model_psnr_threshold": self.effective_model_psnr_threshold,
|
|
150
|
+
"runtime_scorer": CANVAS_RUNTIME_SCORER,
|
|
151
|
+
"runtime_scorer_pixel_exact": True,
|
|
152
|
+
"runtime_scorer_calibration_checkpoints": (
|
|
153
|
+
CANVAS_RUNTIME_CALIBRATION_CHECKPOINTS
|
|
154
|
+
),
|
|
155
|
+
"runtime_scorer_calibration": "data/canvas-checkpoint-parity.json",
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True)
|
|
160
|
+
class OnlineAdaptiveDecision:
|
|
161
|
+
"""Decision made from the pixel-runtime checkpoints observed so far."""
|
|
162
|
+
|
|
163
|
+
stop: bool
|
|
164
|
+
reason: str
|
|
165
|
+
checkpoints_observed: int
|
|
166
|
+
current: CanvasCheckpoint
|
|
167
|
+
selected: CanvasCheckpoint
|
|
168
|
+
config: OnlineAdaptiveConfig
|
|
169
|
+
|
|
170
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
171
|
+
return {
|
|
172
|
+
"mode": "online-observed-only",
|
|
173
|
+
"uses_future_evidence": False,
|
|
174
|
+
"stop": self.stop,
|
|
175
|
+
"reason": self.reason,
|
|
176
|
+
"checkpoints_observed": self.checkpoints_observed,
|
|
177
|
+
"current": dict(self.current.__dict__),
|
|
178
|
+
"selected": dict(self.selected.__dict__),
|
|
179
|
+
"requested_chrome_target": {
|
|
180
|
+
"ssim_srgb": self.config.target_ssim_srgb,
|
|
181
|
+
"psnr_srgb": self.config.target_psnr_srgb,
|
|
182
|
+
},
|
|
183
|
+
"effective_model_threshold": {
|
|
184
|
+
"ssim_srgb": self.config.effective_model_ssim_threshold,
|
|
185
|
+
"psnr_srgb": self.config.effective_model_psnr_threshold,
|
|
186
|
+
},
|
|
187
|
+
"policy": self.config.as_dict(),
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@dataclass(frozen=True)
|
|
192
|
+
class AdaptiveSimulationResult:
|
|
193
|
+
selected: CanvasCheckpoint
|
|
194
|
+
stop_checkpoint: CanvasCheckpoint
|
|
195
|
+
full_run_best: CanvasCheckpoint
|
|
196
|
+
stop_reason: str
|
|
197
|
+
checkpoints_observed: int
|
|
198
|
+
checkpoints_available: int
|
|
199
|
+
observed_stage_sec: float
|
|
200
|
+
full_stage_sec: float
|
|
201
|
+
saved_stage_sec: float
|
|
202
|
+
ssim_opportunity_cost: float
|
|
203
|
+
psnr_opportunity_cost: float
|
|
204
|
+
decisions: Tuple[Dict[str, Any], ...]
|
|
205
|
+
|
|
206
|
+
def as_dict(self) -> Dict[str, Any]:
|
|
207
|
+
return {
|
|
208
|
+
"selected": dict(self.selected.__dict__),
|
|
209
|
+
"stop_checkpoint": dict(self.stop_checkpoint.__dict__),
|
|
210
|
+
"full_run_best": dict(self.full_run_best.__dict__),
|
|
211
|
+
"stop_reason": self.stop_reason,
|
|
212
|
+
"checkpoints_observed": self.checkpoints_observed,
|
|
213
|
+
"checkpoints_available": self.checkpoints_available,
|
|
214
|
+
"observed_stage_sec": self.observed_stage_sec,
|
|
215
|
+
"full_stage_sec": self.full_stage_sec,
|
|
216
|
+
"saved_stage_sec": self.saved_stage_sec,
|
|
217
|
+
"ssim_opportunity_cost": self.ssim_opportunity_cost,
|
|
218
|
+
"psnr_opportunity_cost": self.psnr_opportunity_cost,
|
|
219
|
+
"decisions": list(self.decisions),
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@dataclass(frozen=True)
|
|
224
|
+
class CanvasBudgetPoint:
|
|
225
|
+
image: str
|
|
226
|
+
requested_budget: int
|
|
227
|
+
ssim_srgb: float
|
|
228
|
+
lpips: float
|
|
229
|
+
runtime_sec: float
|
|
230
|
+
artifact_bytes: int
|
|
231
|
+
final_splats: int
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
@dataclass(frozen=True)
|
|
235
|
+
class ScalingPolicy:
|
|
236
|
+
"""Retrospective 2k-to-4k scale decision thresholds."""
|
|
237
|
+
|
|
238
|
+
target_ssim_srgb: float = 0.95
|
|
239
|
+
target_lpips: Optional[float] = 0.15
|
|
240
|
+
min_ssim_gain: float = 0.005
|
|
241
|
+
min_lpips_gain: float = 0.001
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def resolve_online_adaptive_config(
|
|
245
|
+
refinement_config: Mapping[str, Any],
|
|
246
|
+
) -> OnlineAdaptiveConfig:
|
|
247
|
+
"""Resolve and validate the converter's default-off online policy."""
|
|
248
|
+
|
|
249
|
+
enabled_value = refinement_config.get("adaptive_compute_enabled", False)
|
|
250
|
+
if not isinstance(enabled_value, bool):
|
|
251
|
+
raise ValueError("adaptive_compute_enabled must be a boolean")
|
|
252
|
+
target_ssim = refinement_config.get("adaptive_compute_target_ssim_srgb", 0.98)
|
|
253
|
+
target_psnr = refinement_config.get("adaptive_compute_target_psnr_srgb")
|
|
254
|
+
return OnlineAdaptiveConfig(
|
|
255
|
+
enabled=enabled_value,
|
|
256
|
+
min_checkpoints=int(
|
|
257
|
+
refinement_config.get("adaptive_compute_min_checkpoints", 2)
|
|
258
|
+
),
|
|
259
|
+
target_ssim_srgb=(None if target_ssim is None else float(target_ssim)),
|
|
260
|
+
target_psnr_srgb=(None if target_psnr is None else float(target_psnr)),
|
|
261
|
+
chrome_ssim_safety_margin=float(
|
|
262
|
+
refinement_config.get(
|
|
263
|
+
"adaptive_compute_chrome_ssim_margin",
|
|
264
|
+
DEFAULT_CHROME_SSIM_SAFETY_MARGIN,
|
|
265
|
+
)
|
|
266
|
+
),
|
|
267
|
+
chrome_psnr_safety_margin=float(
|
|
268
|
+
refinement_config.get(
|
|
269
|
+
"adaptive_compute_chrome_psnr_margin",
|
|
270
|
+
DEFAULT_CHROME_PSNR_SAFETY_MARGIN,
|
|
271
|
+
)
|
|
272
|
+
),
|
|
273
|
+
checkpoint_min_ssim_gain=float(
|
|
274
|
+
refinement_config.get("canvas_stage_min_ssim_gain", 0.0005)
|
|
275
|
+
),
|
|
276
|
+
max_ssim_regression=float(
|
|
277
|
+
refinement_config.get("canvas_stage_max_ssim_regression", 0.0005)
|
|
278
|
+
),
|
|
279
|
+
max_psnr_regression=float(
|
|
280
|
+
refinement_config.get("canvas_stage_max_psnr_regression", 0.10)
|
|
281
|
+
),
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def evaluate_online_checkpoints(
|
|
286
|
+
checkpoints: Sequence[CanvasCheckpoint],
|
|
287
|
+
config: OnlineAdaptiveConfig,
|
|
288
|
+
) -> OnlineAdaptiveDecision:
|
|
289
|
+
"""Stop only when an observed, selected checkpoint meets the hard target."""
|
|
290
|
+
|
|
291
|
+
if not checkpoints:
|
|
292
|
+
raise ValueError("at least one checkpoint is required")
|
|
293
|
+
policy = config.checkpoint_policy()
|
|
294
|
+
selected = _best_checkpoint(checkpoints, policy)
|
|
295
|
+
if not config.enabled:
|
|
296
|
+
reason = "disabled"
|
|
297
|
+
stop = False
|
|
298
|
+
elif len(checkpoints) < config.min_checkpoints:
|
|
299
|
+
reason = "minimum-checkpoints"
|
|
300
|
+
stop = False
|
|
301
|
+
else:
|
|
302
|
+
ssim_met = (
|
|
303
|
+
config.effective_model_ssim_threshold is None
|
|
304
|
+
or selected.ssim_srgb >= config.effective_model_ssim_threshold
|
|
305
|
+
)
|
|
306
|
+
psnr_met = (
|
|
307
|
+
config.effective_model_psnr_threshold is None
|
|
308
|
+
or selected.psnr_srgb >= config.effective_model_psnr_threshold
|
|
309
|
+
)
|
|
310
|
+
stop = bool(ssim_met and psnr_met)
|
|
311
|
+
reason = "quality-target" if stop else "quality-target-not-met"
|
|
312
|
+
return OnlineAdaptiveDecision(
|
|
313
|
+
stop=stop,
|
|
314
|
+
reason=reason,
|
|
315
|
+
checkpoints_observed=len(checkpoints),
|
|
316
|
+
current=checkpoints[-1],
|
|
317
|
+
selected=selected,
|
|
318
|
+
config=config,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def simulate_adaptive_checkpoints(
|
|
323
|
+
checkpoints: Sequence[CanvasCheckpoint],
|
|
324
|
+
policy: AdaptiveComputePolicy,
|
|
325
|
+
) -> AdaptiveSimulationResult:
|
|
326
|
+
"""Replay a policy over observed checkpoints and report saved stage work."""
|
|
327
|
+
|
|
328
|
+
if not checkpoints:
|
|
329
|
+
raise ValueError("at least one checkpoint is required")
|
|
330
|
+
for checkpoint in checkpoints:
|
|
331
|
+
if checkpoint.elapsed_sec < 0.0:
|
|
332
|
+
raise ValueError("checkpoint elapsed_sec must be non-negative")
|
|
333
|
+
|
|
334
|
+
full_run_best = _best_checkpoint(checkpoints, policy)
|
|
335
|
+
best = checkpoints[0]
|
|
336
|
+
stop_checkpoint = checkpoints[-1]
|
|
337
|
+
stop_reason = "curve-exhausted"
|
|
338
|
+
observed = len(checkpoints)
|
|
339
|
+
decisions = []
|
|
340
|
+
|
|
341
|
+
for index, checkpoint in enumerate(checkpoints):
|
|
342
|
+
accepted, reason = _prefer_checkpoint(checkpoint, best, policy)
|
|
343
|
+
previous_best = best
|
|
344
|
+
if index == 0 or accepted:
|
|
345
|
+
best = checkpoint
|
|
346
|
+
ssim_gain = checkpoint.ssim_srgb - previous_best.ssim_srgb
|
|
347
|
+
psnr_gain = checkpoint.psnr_srgb - previous_best.psnr_srgb
|
|
348
|
+
gain_rate = (
|
|
349
|
+
ssim_gain / checkpoint.elapsed_sec if checkpoint.elapsed_sec > 0.0 else 0.0
|
|
350
|
+
)
|
|
351
|
+
decision = {
|
|
352
|
+
"index": index,
|
|
353
|
+
"checkpoint": checkpoint.label,
|
|
354
|
+
"accepted": bool(index == 0 or accepted),
|
|
355
|
+
"accept_reason": "initial" if index == 0 else reason,
|
|
356
|
+
"selected": best.label,
|
|
357
|
+
"ssim_gain_vs_previous_best": float(ssim_gain),
|
|
358
|
+
"psnr_gain_vs_previous_best": float(psnr_gain),
|
|
359
|
+
"ssim_gain_per_second": float(gain_rate),
|
|
360
|
+
"stop": False,
|
|
361
|
+
"stop_reason": None,
|
|
362
|
+
}
|
|
363
|
+
decisions.append(decision)
|
|
364
|
+
|
|
365
|
+
seen = index + 1
|
|
366
|
+
if seen < policy.min_checkpoints:
|
|
367
|
+
continue
|
|
368
|
+
reason_to_stop = _stop_reason(
|
|
369
|
+
checkpoint=checkpoint,
|
|
370
|
+
selected=best,
|
|
371
|
+
accepted=bool(index == 0 or accepted),
|
|
372
|
+
ssim_gain=ssim_gain,
|
|
373
|
+
psnr_gain=psnr_gain,
|
|
374
|
+
gain_rate=gain_rate,
|
|
375
|
+
policy=policy,
|
|
376
|
+
)
|
|
377
|
+
if reason_to_stop is not None:
|
|
378
|
+
stop_checkpoint = checkpoint
|
|
379
|
+
stop_reason = reason_to_stop
|
|
380
|
+
observed = seen
|
|
381
|
+
decisions[-1]["stop"] = True
|
|
382
|
+
decisions[-1]["stop_reason"] = reason_to_stop
|
|
383
|
+
break
|
|
384
|
+
|
|
385
|
+
observed_seconds = float(
|
|
386
|
+
sum(checkpoint.elapsed_sec for checkpoint in checkpoints[:observed])
|
|
387
|
+
)
|
|
388
|
+
full_seconds = float(sum(checkpoint.elapsed_sec for checkpoint in checkpoints))
|
|
389
|
+
return AdaptiveSimulationResult(
|
|
390
|
+
selected=best,
|
|
391
|
+
stop_checkpoint=stop_checkpoint,
|
|
392
|
+
full_run_best=full_run_best,
|
|
393
|
+
stop_reason=stop_reason,
|
|
394
|
+
checkpoints_observed=observed,
|
|
395
|
+
checkpoints_available=len(checkpoints),
|
|
396
|
+
observed_stage_sec=observed_seconds,
|
|
397
|
+
full_stage_sec=full_seconds,
|
|
398
|
+
saved_stage_sec=max(0.0, full_seconds - observed_seconds),
|
|
399
|
+
ssim_opportunity_cost=max(0.0, full_run_best.ssim_srgb - best.ssim_srgb),
|
|
400
|
+
psnr_opportunity_cost=max(0.0, full_run_best.psnr_srgb - best.psnr_srgb),
|
|
401
|
+
decisions=tuple(decisions),
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def retrospective_scale_decision(
|
|
406
|
+
lower: CanvasBudgetPoint,
|
|
407
|
+
higher: CanvasBudgetPoint,
|
|
408
|
+
policy: ScalingPolicy,
|
|
409
|
+
) -> Dict[str, Any]:
|
|
410
|
+
"""Describe an oracle 2k-to-4k decision from two already-observed points."""
|
|
411
|
+
|
|
412
|
+
if lower.image != higher.image:
|
|
413
|
+
raise ValueError("budget points must describe the same image")
|
|
414
|
+
if lower.requested_budget >= higher.requested_budget:
|
|
415
|
+
raise ValueError("higher budget point must have a larger requested budget")
|
|
416
|
+
|
|
417
|
+
ssim_gain = float(higher.ssim_srgb - lower.ssim_srgb)
|
|
418
|
+
lpips_gain = float(lower.lpips - higher.lpips)
|
|
419
|
+
quality_target_met = lower.ssim_srgb >= policy.target_ssim_srgb and (
|
|
420
|
+
policy.target_lpips is None or lower.lpips <= policy.target_lpips
|
|
421
|
+
)
|
|
422
|
+
if quality_target_met:
|
|
423
|
+
scale = False
|
|
424
|
+
reason = "quality-target"
|
|
425
|
+
elif ssim_gain >= policy.min_ssim_gain and lpips_gain >= policy.min_lpips_gain:
|
|
426
|
+
scale = True
|
|
427
|
+
reason = "observed-quality-gain"
|
|
428
|
+
else:
|
|
429
|
+
scale = False
|
|
430
|
+
reason = "observed-gain-below-threshold"
|
|
431
|
+
return {
|
|
432
|
+
"image": lower.image,
|
|
433
|
+
"mode": "retrospective-oracle",
|
|
434
|
+
"from_budget": lower.requested_budget,
|
|
435
|
+
"to_budget": higher.requested_budget,
|
|
436
|
+
"scale": scale,
|
|
437
|
+
"reason": reason,
|
|
438
|
+
"ssim_gain": ssim_gain,
|
|
439
|
+
"lpips_gain": lpips_gain,
|
|
440
|
+
"runtime_delta_sec": float(higher.runtime_sec - lower.runtime_sec),
|
|
441
|
+
"artifact_delta_bytes": int(higher.artifact_bytes - lower.artifact_bytes),
|
|
442
|
+
"final_splat_delta": int(higher.final_splats - lower.final_splats),
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _best_checkpoint(
|
|
447
|
+
checkpoints: Sequence[CanvasCheckpoint],
|
|
448
|
+
policy: AdaptiveComputePolicy,
|
|
449
|
+
) -> CanvasCheckpoint:
|
|
450
|
+
best = checkpoints[0]
|
|
451
|
+
for checkpoint in checkpoints[1:]:
|
|
452
|
+
accepted, _ = _prefer_checkpoint(checkpoint, best, policy)
|
|
453
|
+
if accepted:
|
|
454
|
+
best = checkpoint
|
|
455
|
+
return best
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def _prefer_checkpoint(
|
|
459
|
+
candidate: CanvasCheckpoint,
|
|
460
|
+
incumbent: CanvasCheckpoint,
|
|
461
|
+
policy: AdaptiveComputePolicy,
|
|
462
|
+
) -> Tuple[bool, str]:
|
|
463
|
+
psnr_safe = candidate.psnr_srgb >= incumbent.psnr_srgb - policy.max_psnr_regression
|
|
464
|
+
if candidate.ssim_srgb >= incumbent.ssim_srgb + policy.checkpoint_min_ssim_gain:
|
|
465
|
+
return (psnr_safe, "material-ssim-gain" if psnr_safe else "psnr-gate")
|
|
466
|
+
if (
|
|
467
|
+
candidate.splat_count < incumbent.splat_count
|
|
468
|
+
and candidate.ssim_srgb >= incumbent.ssim_srgb - policy.max_ssim_regression
|
|
469
|
+
and psnr_safe
|
|
470
|
+
):
|
|
471
|
+
return True, "smaller-equivalent"
|
|
472
|
+
if candidate.ssim_srgb < incumbent.ssim_srgb - policy.max_ssim_regression:
|
|
473
|
+
return False, "ssim-regression"
|
|
474
|
+
if not psnr_safe:
|
|
475
|
+
return False, "psnr-regression"
|
|
476
|
+
return False, "gain-below-checkpoint-threshold"
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _stop_reason(
|
|
480
|
+
*,
|
|
481
|
+
checkpoint: CanvasCheckpoint,
|
|
482
|
+
selected: CanvasCheckpoint,
|
|
483
|
+
accepted: bool,
|
|
484
|
+
ssim_gain: float,
|
|
485
|
+
psnr_gain: float,
|
|
486
|
+
gain_rate: float,
|
|
487
|
+
policy: AdaptiveComputePolicy,
|
|
488
|
+
) -> Optional[str]:
|
|
489
|
+
target_ssim_met = (
|
|
490
|
+
policy.target_ssim_srgb is None or selected.ssim_srgb >= policy.target_ssim_srgb
|
|
491
|
+
)
|
|
492
|
+
target_psnr_met = (
|
|
493
|
+
policy.target_psnr_srgb is None or selected.psnr_srgb >= policy.target_psnr_srgb
|
|
494
|
+
)
|
|
495
|
+
target_enabled = (
|
|
496
|
+
policy.target_ssim_srgb is not None or policy.target_psnr_srgb is not None
|
|
497
|
+
)
|
|
498
|
+
if target_enabled and target_ssim_met and target_psnr_met:
|
|
499
|
+
return "quality-target"
|
|
500
|
+
if (
|
|
501
|
+
policy.stop_on_regression
|
|
502
|
+
and not accepted
|
|
503
|
+
and (
|
|
504
|
+
checkpoint.ssim_srgb < selected.ssim_srgb - policy.max_ssim_regression
|
|
505
|
+
or checkpoint.psnr_srgb < selected.psnr_srgb - policy.max_psnr_regression
|
|
506
|
+
)
|
|
507
|
+
):
|
|
508
|
+
return "regression-revert"
|
|
509
|
+
if (
|
|
510
|
+
policy.stop_on_plateau
|
|
511
|
+
and ssim_gain < policy.plateau_min_ssim_gain
|
|
512
|
+
and psnr_gain < policy.plateau_min_psnr_gain
|
|
513
|
+
):
|
|
514
|
+
return "plateau"
|
|
515
|
+
if (
|
|
516
|
+
policy.min_ssim_gain_per_second > 0.0
|
|
517
|
+
and gain_rate < policy.min_ssim_gain_per_second
|
|
518
|
+
):
|
|
519
|
+
return "low-marginal-return"
|
|
520
|
+
return None
|