Files
LUCAS M MORGAN dfd7e68cbb Self-contained SHARP: vendor sharp_nodes, gsplat bundle, licenses.
Merge LoadSharpModel and SharpPredict into this pack; register all nodes from __init__.py. Bundle web/js/gsplat-bundle.js and resolve viewer load via import.meta.url. Add LICENSE, THIRD_PARTY_NOTICES, and GPL-3.0 text. Update README and requirements (huggingface-hub). Remove GeometryPack runtime dependency for the WebGL viewer.

Made-with: Cursor
2026-04-01 14:51:26 -05:00

492 lines
15 KiB
Python

"""SHARP model: params, config presets, and factory functions.
Consolidates params.py, presets/, and all create_* factories into one file.
Exports PredictorParams and create_predictor for use by load_model.py.
For licensing see accompanying LICENSE file.
Copyright (C) 2025 Apple Inc. All Rights Reserved.
"""
from __future__ import annotations
import dataclasses
import logging
from typing import Literal
from .color_space import ColorSpace
from .model import (
DirectPredictionHead,
GaussianComposer,
GaussianDensePredictionTransformer,
LearnedAlignment,
MonodepthDensePredictionTransformer,
MonodepthWithEncodingAdaptor,
MultiLayerInitializer,
MultiresConvDecoder,
NormLayerName,
RGBGaussianPredictor,
SlidingPyramidNetwork,
UpsamplingMode,
VisionTransformer,
)
LOGGER = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# ViT Presets
# ---------------------------------------------------------------------------
ViTPreset = Literal["dinov2l16_384"]
MLPMode = Literal["vanilla", "glu"]
@dataclasses.dataclass
class ViTConfig:
"""Configuration for ViT."""
in_chans: int
embed_dim: int
depth: int
num_heads: int
init_values: float
img_size: int = 384
patch_size: int = 16
num_classes: int = 21841
mlp_ratio: float = 4.0
qkv_bias: bool = True
global_pool: str = "avg"
mlp_mode: MLPMode = "vanilla"
intermediate_features_ids: list[int] | None = None
VIT_CONFIG_DICT: dict[ViTPreset, ViTConfig] = {
"dinov2l16_384": ViTConfig(
in_chans=3,
embed_dim=1024,
depth=24,
num_heads=16,
init_values=1e-5,
global_pool="",
),
}
# ---------------------------------------------------------------------------
# Monodepth Presets
# ---------------------------------------------------------------------------
MONODEPTH_ENCODER_DIMS_MAP: dict[ViTPreset, list[int]] = {
"dinov2l16_384": [256, 512, 1024, 1024],
}
MONODEPTH_HOOK_IDS_MAP: dict[ViTPreset, list[int]] = {
"dinov2l16_384": [5, 11, 17, 23],
}
# ---------------------------------------------------------------------------
# Parameter Dataclasses
# ---------------------------------------------------------------------------
DimsDecoder = tuple[int, int, int, int, int]
DPTImageEncoderType = Literal["skip_conv", "skip_conv_kernel2"]
ColorInitOption = Literal["none", "first_layer", "all_layers"]
DepthInitOption = Literal["surface_min", "surface_max", "base_depth", "linear_disparity"]
@dataclasses.dataclass
class AlignmentParams:
"""Parameters for depth alignment."""
kernel_size: int = 16
stride: int = 1
frozen: bool = False
steps: int = 4
activation_type: str = "exp"
depth_decoder_features: bool = False
base_width: int = 16
@dataclasses.dataclass
class DeltaFactor:
"""Factors to multiply deltas with before activation."""
xy: float = 0.001
z: float = 0.001
color: float = 0.1
opacity: float = 1.0
scale: float = 1.0
quaternion: float = 1.0
@dataclasses.dataclass
class InitializerParams:
"""Parameters for initializer."""
scale_factor: float = 1.0
disparity_factor: float = 1.0
stride: int = 2
num_layers: int = 2
first_layer_depth_option: DepthInitOption = "surface_min"
rest_layer_depth_option: DepthInitOption = "surface_min"
color_option: ColorInitOption = "all_layers"
base_depth: float = 10.0
feature_input_stop_grad: bool = False
normalize_depth: bool = True
output_inpainted_layer_only: bool = False
set_uninpainted_opacity_to_zero: bool = False
concat_inpainting_mask: bool = False
@dataclasses.dataclass
class MonodepthParams:
"""Parameters for monodepth network."""
patch_encoder_preset: ViTPreset = "dinov2l16_384"
image_encoder_preset: ViTPreset = "dinov2l16_384"
checkpoint_uri: str | None = None
unfreeze_patch_encoder: bool = False
unfreeze_image_encoder: bool = False
unfreeze_decoder: bool = False
unfreeze_head: bool = False
unfreeze_norm_layers: bool = False
grad_checkpointing: bool = False
use_patch_overlap: bool = True
dims_decoder: DimsDecoder = (256, 256, 256, 256, 256)
@dataclasses.dataclass
class MonodepthAdaptorParams:
"""Parameters for monodepth network feature adaptor."""
encoder_features: bool = True
decoder_features: bool = False
@dataclasses.dataclass
class GaussianDecoderParams:
"""Parameters for backbone with default values."""
dim_in: int = 5
dim_out: int = 32
norm_type: NormLayerName = "group_norm"
norm_num_groups: int = 8
stride: int = 2
patch_encoder_preset: ViTPreset = "dinov2l16_384"
image_encoder_preset: ViTPreset = "dinov2l16_384"
dims_decoder: DimsDecoder = (128, 128, 128, 128, 128)
use_depth_input: bool = True
grad_checkpointing: bool = False
upsampling_mode: UpsamplingMode = "transposed_conv"
image_encoder_type: DPTImageEncoderType = "skip_conv_kernel2"
@dataclasses.dataclass
class PredictorParams:
"""Parameters for predictors with default values."""
initializer: InitializerParams = dataclasses.field(default_factory=InitializerParams)
monodepth: MonodepthParams = dataclasses.field(default_factory=MonodepthParams)
monodepth_adaptor: MonodepthAdaptorParams = dataclasses.field(
default_factory=MonodepthAdaptorParams
)
gaussian_decoder: GaussianDecoderParams = dataclasses.field(
default_factory=GaussianDecoderParams
)
depth_alignment: AlignmentParams = dataclasses.field(default_factory=AlignmentParams)
delta_factor: DeltaFactor = dataclasses.field(default_factory=DeltaFactor)
max_scale: float = 10.0
min_scale: float = 0.0
norm_type: NormLayerName = "group_norm"
norm_num_groups: int = 8
use_predicted_mean: bool = False
color_activation_type: str = "sigmoid"
opacity_activation_type: str = "sigmoid"
color_space: ColorSpace = "linearRGB"
low_pass_filter_eps: float = 1e-2
num_monodepth_layers: int = 2
sorting_monodepth: bool = False
base_scale_on_predicted_mean: bool = True
# ---------------------------------------------------------------------------
# Factory Functions
# ---------------------------------------------------------------------------
def create_vit(
config: ViTConfig | None = None,
preset: ViTPreset | None = "dinov2l16_384",
intermediate_features_ids: list[int] | None = None,
dtype=None,
device=None,
operations=None,
) -> VisionTransformer:
"""Factory function for creating a ViT model."""
if config is not None:
LOGGER.info("Using user-defined config.")
else:
if preset is None:
raise ValueError("User-defined config and preset cannot be both None.")
LOGGER.info("Using preset ViT %s.", preset)
config = VIT_CONFIG_DICT[preset]
config.intermediate_features_ids = intermediate_features_ids
model = VisionTransformer(
img_size=config.img_size,
patch_size=config.patch_size,
in_chans=config.in_chans,
embed_dim=config.embed_dim,
depth=config.depth,
num_heads=config.num_heads,
mlp_ratio=config.mlp_ratio,
qkv_bias=config.qkv_bias,
init_values=config.init_values,
use_glu_mlp=(config.mlp_mode == "glu"),
num_classes=config.num_classes,
intermediate_features_ids=config.intermediate_features_ids,
dtype=dtype,
device=device,
operations=operations,
)
return model
def create_monodepth_encoder(
patch_encoder_preset: ViTPreset,
image_encoder_preset: ViTPreset,
use_patch_overlap: bool = True,
last_encoder: int = 256,
dtype=None,
device=None,
operations=None,
) -> SlidingPyramidNetwork:
"""Create the SPN-based monodepth encoder."""
dims_encoder = [last_encoder] + MONODEPTH_ENCODER_DIMS_MAP[patch_encoder_preset]
patch_encoder_block_ids = MONODEPTH_HOOK_IDS_MAP[patch_encoder_preset]
patch_encoder = create_vit(
preset=patch_encoder_preset,
intermediate_features_ids=patch_encoder_block_ids,
dtype=dtype, device=device, operations=operations,
)
image_encoder = create_vit(
preset=image_encoder_preset,
intermediate_features_ids=None,
dtype=dtype, device=device, operations=operations,
)
return SlidingPyramidNetwork(
dims_encoder=dims_encoder,
patch_encoder=patch_encoder,
image_encoder=image_encoder,
use_patch_overlap=use_patch_overlap,
dtype=dtype, device=device, operations=operations,
)
def create_monodepth_decoder(
patch_encoder_preset: ViTPreset,
dims_decoder=None,
dtype=None,
device=None,
operations=None,
) -> MultiresConvDecoder:
"""Create monodepth decoder."""
dims_encoder = MONODEPTH_ENCODER_DIMS_MAP[patch_encoder_preset]
if dims_decoder is None:
dims_decoder = dims_encoder[0]
if isinstance(dims_decoder, int):
dims_decoder = [dims_decoder]
return MultiresConvDecoder(
dims_encoder=[dims_decoder[0]] + list(dims_encoder),
dims_decoder=dims_decoder,
dtype=dtype, device=device, operations=operations,
)
def create_monodepth_dpt(
params: MonodepthParams | None = None,
dtype=None,
device=None,
operations=None,
) -> MonodepthDensePredictionTransformer:
"""Create MonodepthDensePredictionTransformer model."""
if params is None:
params = MonodepthParams()
encoder = create_monodepth_encoder(
params.patch_encoder_preset,
params.image_encoder_preset,
use_patch_overlap=params.use_patch_overlap,
last_encoder=params.dims_decoder[0],
dtype=dtype, device=device, operations=operations,
)
decoder = create_monodepth_decoder(
params.patch_encoder_preset, params.dims_decoder,
dtype=dtype, device=device, operations=operations,
)
return MonodepthDensePredictionTransformer(
encoder=encoder, decoder=decoder, last_dims=(32, 1),
dtype=dtype, device=device, operations=operations,
)
def create_monodepth_adaptor(
monodepth_predictor: MonodepthDensePredictionTransformer,
params: MonodepthAdaptorParams,
num_monodepth_layers: int,
sorting_monodepth: bool,
) -> MonodepthWithEncodingAdaptor:
"""Create an adaptor that returns both disparity and features."""
return MonodepthWithEncodingAdaptor(
monodepth_predictor=monodepth_predictor,
return_encoder_features=params.encoder_features,
return_decoder_features=params.decoder_features,
num_monodepth_layers=num_monodepth_layers,
sorting_monodepth=sorting_monodepth,
)
def create_gaussian_decoder(
params: GaussianDecoderParams,
dims_depth_features: list[int],
dtype=None,
device=None,
operations=None,
) -> GaussianDensePredictionTransformer:
"""Create gaussian_decoder model."""
decoder = MultiresConvDecoder(
dims_depth_features,
params.dims_decoder,
upsampling_mode=params.upsampling_mode,
dtype=dtype, device=device, operations=operations,
)
return GaussianDensePredictionTransformer(
decoder=decoder,
dim_in=params.dim_in,
dim_out=params.dim_out,
stride_out=params.stride,
norm_type=params.norm_type,
norm_num_groups=params.norm_num_groups,
use_depth_input=params.use_depth_input,
image_encoder_type=params.image_encoder_type,
image_encoder_params=params,
dtype=dtype, device=device, operations=operations,
)
def create_initializer(params: InitializerParams) -> MultiLayerInitializer:
"""Create initializer."""
return MultiLayerInitializer(
num_layers=params.num_layers,
stride=params.stride,
base_depth=params.base_depth,
scale_factor=params.scale_factor,
disparity_factor=params.disparity_factor,
color_option=params.color_option,
first_layer_depth_option=params.first_layer_depth_option,
rest_layer_depth_option=params.rest_layer_depth_option,
normalize_depth=params.normalize_depth,
feature_input_stop_grad=params.feature_input_stop_grad,
)
def create_alignment(
params: AlignmentParams,
depth_decoder_dim: int | None = None,
dtype=None,
device=None,
operations=None,
) -> LearnedAlignment | None:
"""Create depth alignment."""
if depth_decoder_dim is None:
raise ValueError("Requires depth_decoder_dim for LearnedAlignment.")
return LearnedAlignment(
depth_decoder_features=params.depth_decoder_features,
depth_decoder_dim=depth_decoder_dim,
steps=params.steps,
stride=params.stride,
base_width=params.base_width,
activation_type=params.activation_type,
dtype=dtype, device=device, operations=operations,
)
def create_predictor(
params: PredictorParams,
dtype=None,
device=None,
operations=None,
) -> RGBGaussianPredictor:
"""Create gaussian predictor model.
This is the top-level factory called by load_model.py.
Accepts dtype, device, operations for ComfyUI-native weight management.
"""
if params.gaussian_decoder.stride < params.initializer.stride:
raise ValueError(
"We donot expected gaussian_decoder has higher resolution than initializer."
)
scale_factor = params.gaussian_decoder.stride // params.initializer.stride
gaussian_composer = GaussianComposer(
delta_factor=params.delta_factor,
min_scale=params.min_scale,
max_scale=params.max_scale,
color_activation_type=params.color_activation_type,
opacity_activation_type=params.opacity_activation_type,
color_space=params.color_space,
scale_factor=scale_factor,
base_scale_on_predicted_mean=params.base_scale_on_predicted_mean,
)
if params.num_monodepth_layers > 1 and params.initializer.num_layers != 2:
raise KeyError("We only support num_layers = 2 when num_monodepth_layers > 1.")
monodepth_model = create_monodepth_dpt(
params.monodepth,
dtype=dtype, device=device, operations=operations,
)
monodepth_adaptor = create_monodepth_adaptor(
monodepth_model,
params.monodepth_adaptor,
params.num_monodepth_layers,
params.sorting_monodepth,
)
if params.num_monodepth_layers == 2:
monodepth_adaptor.replicate_head(params.num_monodepth_layers)
gaussian_decoder = create_gaussian_decoder(
params.gaussian_decoder,
dims_depth_features=monodepth_adaptor.get_feature_dims(),
dtype=dtype, device=device, operations=operations,
)
initializer = create_initializer(params.initializer)
prediction_head = DirectPredictionHead(
feature_dim=gaussian_decoder.dim_out, num_layers=initializer.num_layers,
dtype=dtype, device=device, operations=operations,
)
decoder_dim = monodepth_model.decoder.dims_decoder[-1]
return RGBGaussianPredictor(
init_model=initializer,
feature_model=gaussian_decoder,
prediction_head=prediction_head,
monodepth_model=monodepth_adaptor,
gaussian_composer=gaussian_composer,
scale_map_estimator=create_alignment(
params.depth_alignment, depth_decoder_dim=decoder_dim,
dtype=dtype, device=device, operations=operations,
),
dtype=dtype,
)
__all__ = [
"PredictorParams",
"create_predictor",
]