# coding=utf-8 # Copyright 2024 Intel Labs and The HuggingFace Inc. team. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """PyTorch ZoeDepth model.""" import math from dataclasses import dataclass from typing import List, Optional, Tuple, Union import torch import torch.utils.checkpoint from torch import nn from ...activations import ACT2FN from ...file_utils import ( add_start_docstrings, add_start_docstrings_to_model_forward, replace_return_docstrings, ) from ...modeling_outputs import DepthEstimatorOutput from ...modeling_utils import PreTrainedModel from ...utils import ModelOutput, logging from ...utils.backbone_utils import load_backbone from .configuration_zoedepth import ZoeDepthConfig logger = logging.get_logger(__name__) # General docstring _CONFIG_FOR_DOC = "ZoeDepthConfig" @dataclass class ZoeDepthDepthEstimatorOutput(ModelOutput): """ Extension of `DepthEstimatorOutput` to include domain logits (ZoeDepth specific). Args: loss (`torch.FloatTensor` of shape `(1,)`, *optional*, returned when `labels` is provided): Classification (or regression if config.num_labels==1) loss. predicted_depth (`torch.FloatTensor` of shape `(batch_size, height, width)`): Predicted depth for each pixel. domain_logits (`torch.FloatTensor` of shape `(batch_size, num_domains)`): Logits for each domain (e.g. NYU and KITTI) in case multiple metric heads are used. hidden_states (`tuple(torch.FloatTensor)`, *optional*, returned when `output_hidden_states=True` is passed or when `config.output_hidden_states=True`): Tuple of `torch.FloatTensor` (one for the output of the embeddings, if the model has an embedding layer, + one for the output of each layer) of shape `(batch_size, num_channels, height, width)`. Hidden-states of the model at the output of each layer plus the optional initial embedding outputs. attentions (`tuple(torch.FloatTensor)`, *optional*, returned when `output_attentions=True` is passed or when `config.output_attentions=True`): Tuple of `torch.FloatTensor` (one for each layer) of shape `(batch_size, num_heads, patch_size, sequence_length)`. Attentions weights after the attention softmax, used to compute the weighted average in the self-attention heads. """ loss: Optional[torch.FloatTensor] = None predicted_depth: torch.FloatTensor = None domain_logits: torch.FloatTensor = None hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None attentions: Optional[Tuple[torch.FloatTensor, ...]] = None class ZoeDepthReassembleStage(nn.Module): """ This class reassembles the hidden states of the backbone into image-like feature representations at various resolutions. This happens in 3 stages: 1. Map the N + 1 tokens to a set of N tokens, by taking into account the readout ([CLS]) token according to `config.readout_type`. 2. Project the channel dimension of the hidden states according to `config.neck_hidden_sizes`. 3. Resizing the spatial dimensions (height, width). Args: config (`[ZoeDepthConfig]`): Model configuration class defining the model architecture. """ def __init__(self, config): super().__init__() self.readout_type = config.readout_type self.layers = nn.ModuleList() for neck_hidden_size, factor in zip(config.neck_hidden_sizes, config.reassemble_factors): self.layers.append(ZoeDepthReassembleLayer(config, channels=neck_hidden_size, factor=factor)) if config.readout_type == "project": self.readout_projects = nn.ModuleList() hidden_size = config.backbone_hidden_size for _ in config.neck_hidden_sizes: self.readout_projects.append( nn.Sequential(nn.Linear(2 * hidden_size, hidden_size), ACT2FN[config.hidden_act]) ) def forward(self, hidden_states: List[torch.Tensor], patch_height, patch_width) -> List[torch.Tensor]: """ Args: hidden_states (`List[torch.FloatTensor]`, each of shape `(batch_size, sequence_length + 1, hidden_size)`): List of hidden states from the backbone. """ batch_size = hidden_states[0].shape[0] # stack along batch dimension # shape (batch_size*num_stages, sequence_length + 1, hidden_size) hidden_states = torch.cat(hidden_states, dim=0) cls_token, hidden_states = hidden_states[:, 0], hidden_states[:, 1:] # reshape hidden_states to (batch_size*num_stages, num_channels, height, width) total_batch_size, sequence_length, num_channels = hidden_states.shape hidden_states = hidden_states.reshape(total_batch_size, patch_height, patch_width, num_channels) hidden_states = hidden_states.permute(0, 3, 1, 2).contiguous() if self.readout_type == "project": # reshape to (batch_size*num_stages, height*width, num_channels) hidden_states = hidden_states.flatten(2).permute((0, 2, 1)) readout = cls_token.unsqueeze(dim=1).expand_as(hidden_states) # concatenate the readout token to the hidden states # to get (batch_size*num_stages, height*width, 2*num_channels) hidden_states = torch.cat((hidden_states, readout), -1) elif self.readout_type == "add": hidden_states = hidden_states + cls_token.unsqueeze(-1) out = [] for stage_idx, hidden_state in enumerate(hidden_states.split(batch_size, dim=0)): if self.readout_type == "project": hidden_state = self.readout_projects[stage_idx](hidden_state) # reshape back to (batch_size, num_channels, height, width) hidden_state = hidden_state.permute(0, 2, 1).reshape(batch_size, -1, patch_height, patch_width) hidden_state = self.layers[stage_idx](hidden_state) out.append(hidden_state) return out class ZoeDepthReassembleLayer(nn.Module): def __init__(self, config, channels, factor): super().__init__() # projection hidden_size = config.backbone_hidden_size self.projection = nn.Conv2d(in_channels=hidden_size, out_channels=channels, kernel_size=1) # up/down sampling depending on factor if factor > 1: self.resize = nn.ConvTranspose2d(channels, channels, kernel_size=factor, stride=factor, padding=0) elif factor == 1: self.resize = nn.Identity() elif factor < 1: # so should downsample self.resize = nn.Conv2d(channels, channels, kernel_size=3, stride=int(1 / factor), padding=1) # Copied from transformers.models.dpt.modeling_dpt.DPTReassembleLayer.forward with DPT->ZoeDepth def forward(self, hidden_state): hidden_state = self.projection(hidden_state) hidden_state = self.resize(hidden_state) return hidden_state # Copied from transformers.models.dpt.modeling_dpt.DPTFeatureFusionStage with DPT->ZoeDepth class ZoeDepthFeatureFusionStage(nn.Module): def __init__(self, config): super().__init__() self.layers = nn.ModuleList() for _ in range(len(config.neck_hidden_sizes)): self.layers.append(ZoeDepthFeatureFusionLayer(config)) def forward(self, hidden_states): # reversing the hidden_states, we start from the last hidden_states = hidden_states[::-1] fused_hidden_states = [] # first layer only uses the last hidden_state fused_hidden_state = self.layers[0](hidden_states[0]) fused_hidden_states.append(fused_hidden_state) # looping from the last layer to the second for hidden_state, layer in zip(hidden_states[1:], self.layers[1:]): fused_hidden_state = layer(fused_hidden_state, hidden_state) fused_hidden_states.append(fused_hidden_state) return fused_hidden_states # Copied from transformers.models.dpt.modeling_dpt.DPTPreActResidualLayer with DPT->ZoeDepth class ZoeDepthPreActResidualLayer(nn.Module): """ ResidualConvUnit, pre-activate residual unit. Args: config (`[ZoeDepthConfig]`): Model configuration class defining the model architecture. """ # Ignore copy def __init__(self, config): super().__init__() self.use_batch_norm = config.use_batch_norm_in_fusion_residual use_bias_in_fusion_residual = ( config.use_bias_in_fusion_residual if config.use_bias_in_fusion_residual is not None else not self.use_batch_norm ) self.activation1 = nn.ReLU() self.convolution1 = nn.Conv2d( config.fusion_hidden_size, config.fusion_hidden_size, kernel_size=3, stride=1, padding=1, bias=use_bias_in_fusion_residual, ) self.activation2 = nn.ReLU() self.convolution2 = nn.Conv2d( config.fusion_hidden_size, config.fusion_hidden_size, kernel_size=3, stride=1, padding=1, bias=use_bias_in_fusion_residual, ) if self.use_batch_norm: self.batch_norm1 = nn.BatchNorm2d(config.fusion_hidden_size, eps=config.batch_norm_eps) self.batch_norm2 = nn.BatchNorm2d(config.fusion_hidden_size, eps=config.batch_norm_eps) def forward(self, hidden_state: torch.Tensor) -> torch.Tensor: residual = hidden_state hidden_state = self.activation1(hidden_state) hidden_state = self.convolution1(hidden_state) if self.use_batch_norm: hidden_state = self.batch_norm1(hidden_state) hidden_state = self.activation2(hidden_state) hidden_state = self.convolution2(hidden_state) if self.use_batch_norm: hidden_state = self.batch_norm2(hidden_state) return hidden_state + residual # Copied from transformers.models.dpt.modeling_dpt.DPTFeatureFusionLayer with DPT->ZoeDepth class ZoeDepthFeatureFusionLayer(nn.Module): """Feature fusion layer, merges feature maps from different stages. Args: config (`[ZoeDepthConfig]`): Model configuration class defining the model architecture. align_corners (`bool`, *optional*, defaults to `True`): The align_corner setting for bilinear upsample. """ def __init__(self, config, align_corners=True): super().__init__() self.align_corners = align_corners self.projection = nn.Conv2d(config.fusion_hidden_size, config.fusion_hidden_size, kernel_size=1, bias=True) self.residual_layer1 = ZoeDepthPreActResidualLayer(config) self.residual_layer2 = ZoeDepthPreActResidualLayer(config) def forward(self, hidden_state, residual=None): if residual is not None: if hidden_state.shape != residual.shape: residual = nn.functional.interpolate( residual, size=(hidden_state.shape[2], hidden_state.shape[3]), mode="bilinear", align_corners=False ) hidden_state = hidden_state + self.residual_layer1(residual) hidden_state = self.residual_layer2(hidden_state) hidden_state = nn.functional.interpolate( hidden_state, scale_factor=2, mode="bilinear", align_corners=self.align_corners ) hidden_state = self.projection(hidden_state) return hidden_state class ZoeDepthNeck(nn.Module): """ ZoeDepthNeck. A neck is a module that is normally used between the backbone and the head. It takes a list of tensors as input and produces another list of tensors as output. For ZoeDepth, it includes 2 stages: * ZoeDepthReassembleStage * ZoeDepthFeatureFusionStage. Args: config (dict): config dict. """ # Copied from transformers.models.dpt.modeling_dpt.DPTNeck.__init__ with DPT->ZoeDepth def __init__(self, config): super().__init__() self.config = config # postprocessing: only required in case of a non-hierarchical backbone (e.g. ViT, BEiT) if config.backbone_config is not None and config.backbone_config.model_type in ["swinv2"]: self.reassemble_stage = None else: self.reassemble_stage = ZoeDepthReassembleStage(config) self.convs = nn.ModuleList() for channel in config.neck_hidden_sizes: self.convs.append(nn.Conv2d(channel, config.fusion_hidden_size, kernel_size=3, padding=1, bias=False)) # fusion self.fusion_stage = ZoeDepthFeatureFusionStage(config) def forward(self, hidden_states: List[torch.Tensor], patch_height, patch_width) -> List[torch.Tensor]: """ Args: hidden_states (`List[torch.FloatTensor]`, each of shape `(batch_size, sequence_length, hidden_size)` or `(batch_size, hidden_size, height, width)`): List of hidden states from the backbone. """ if not isinstance(hidden_states, (tuple, list)): raise TypeError("hidden_states should be a tuple or list of tensors") if len(hidden_states) != len(self.config.neck_hidden_sizes): raise ValueError("The number of hidden states should be equal to the number of neck hidden sizes.") # postprocess hidden states if self.reassemble_stage is not None: hidden_states = self.reassemble_stage(hidden_states, patch_height, patch_width) features = [self.convs[i](feature) for i, feature in enumerate(hidden_states)] # fusion blocks output = self.fusion_stage(features) return output, features[-1] class ZoeDepthRelativeDepthEstimationHead(nn.Module): """ Relative depth estimation head consisting of 3 convolutional layers. It progressively halves the feature dimension and upsamples the predictions to the input resolution after the first convolutional layer (details can be found in DPT's paper's supplementary material). """ def __init__(self, config): super().__init__() self.head_in_index = config.head_in_index self.projection = None if config.add_projection: self.projection = nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)) features = config.fusion_hidden_size self.conv1 = nn.Conv2d(features, features // 2, kernel_size=3, stride=1, padding=1) self.upsample = nn.Upsample(scale_factor=2, mode="bilinear", align_corners=True) self.conv2 = nn.Conv2d(features // 2, config.num_relative_features, kernel_size=3, stride=1, padding=1) self.conv3 = nn.Conv2d(config.num_relative_features, 1, kernel_size=1, stride=1, padding=0) def forward(self, hidden_states: List[torch.Tensor]) -> torch.Tensor: # use last features hidden_states = hidden_states[self.head_in_index] if self.projection is not None: hidden_states = self.projection(hidden_states) hidden_states = nn.ReLU()(hidden_states) hidden_states = self.conv1(hidden_states) hidden_states = self.upsample(hidden_states) hidden_states = self.conv2(hidden_states) hidden_states = nn.ReLU()(hidden_states) # we need the features here (after second conv + ReLu) features = hidden_states hidden_states = self.conv3(hidden_states) hidden_states = nn.ReLU()(hidden_states) predicted_depth = hidden_states.squeeze(dim=1) return predicted_depth, features def log_binom(n, k, eps=1e-7): """log(nCk) using stirling approximation""" n = n + eps k = k + eps return n * torch.log(n) - k * torch.log(k) - (n - k) * torch.log(n - k + eps) class LogBinomialSoftmax(nn.Module): def __init__(self, n_classes=256, act=torch.softmax): """Compute log binomial distribution for n_classes Args: n_classes (`int`, *optional*, defaults to 256): Number of output classes. act (`torch.nn.Module`, *optional*, defaults to `torch.softmax`): Activation function to apply to the output. """ super().__init__() self.k = n_classes self.act = act self.register_buffer("k_idx", torch.arange(0, n_classes).view(1, -1, 1, 1), persistent=False) self.register_buffer("k_minus_1", torch.Tensor([self.k - 1]).view(1, -1, 1, 1), persistent=False) def forward(self, probabilities, temperature=1.0, eps=1e-4): """Compute the log binomial distribution for probabilities. Args: probabilities (`torch.Tensor` of shape `(batch_size, num_channels, height, width)`): Tensor containing probabilities of each class. temperature (`float` or `torch.Tensor` of shape `(batch_size, num_channels, height, width)`, *optional*, defaults to 1): Temperature of distribution. eps (`float`, *optional*, defaults to 1e-4): Small number for numerical stability. Returns: `torch.Tensor` of shape `(batch_size, num_channels, height, width)`: Log binomial distribution logbinomial(p;t). """ if probabilities.ndim == 3: probabilities = probabilities.unsqueeze(1) # make it (batch_size, num_channels, height, width) one_minus_probabilities = torch.clamp(1 - probabilities, eps, 1) probabilities = torch.clamp(probabilities, eps, 1) y = ( log_binom(self.k_minus_1, self.k_idx) + self.k_idx * torch.log(probabilities) + (self.k_minus_1 - self.k_idx) * torch.log(one_minus_probabilities) ) return self.act(y / temperature, dim=1) class ZoeDepthConditionalLogBinomialSoftmax(nn.Module): def __init__( self, config, in_features, condition_dim, n_classes=256, bottleneck_factor=2, ): """Per-pixel MLP followed by a Conditional Log Binomial softmax. Args: in_features (`int`): Number of input channels in the main feature. condition_dim (`int`): Number of input channels in the condition feature. n_classes (`int`, *optional*, defaults to 256): Number of classes. bottleneck_factor (`int`, *optional*, defaults to 2): Hidden dim factor. """ super().__init__() bottleneck = (in_features + condition_dim) // bottleneck_factor self.mlp = nn.Sequential( nn.Conv2d(in_features + condition_dim, bottleneck, kernel_size=1, stride=1, padding=0), nn.GELU(), # 2 for probabilities linear norm, 2 for temperature linear norm nn.Conv2d(bottleneck, 2 + 2, kernel_size=1, stride=1, padding=0), nn.Softplus(), ) self.p_eps = 1e-4 self.max_temp = config.max_temp self.min_temp = config.min_temp self.log_binomial_transform = LogBinomialSoftmax(n_classes, act=torch.softmax) def forward(self, main_feature, condition_feature): """ Args: main_feature (`torch.Tensor` of shape `(batch_size, num_channels, height, width)`): Main feature. condition_feature (torch.Tensor of shape `(batch_size, num_channels, height, width)`): Condition feature. Returns: `torch.Tensor`: Output log binomial distribution """ probabilities_and_temperature = self.mlp(torch.concat((main_feature, condition_feature), dim=1)) probabilities, temperature = ( probabilities_and_temperature[:, :2, ...], probabilities_and_temperature[:, 2:, ...], ) probabilities = probabilities + self.p_eps probabilities = probabilities[:, 0, ...] / (probabilities[:, 0, ...] + probabilities[:, 1, ...]) temperature = temperature + self.p_eps temperature = temperature[:, 0, ...] / (temperature[:, 0, ...] + temperature[:, 1, ...]) temperature = temperature.unsqueeze(1) temperature = (self.max_temp - self.min_temp) * temperature + self.min_temp return self.log_binomial_transform(probabilities, temperature) class ZoeDepthSeedBinRegressor(nn.Module): def __init__(self, config, n_bins=16, mlp_dim=256, min_depth=1e-3, max_depth=10): """Bin center regressor network. Can be "normed" or "unnormed". If "normed", bin centers are bounded on the (min_depth, max_depth) interval. Args: config (`int`): Model configuration. n_bins (`int`, *optional*, defaults to 16): Number of bin centers. mlp_dim (`int`, *optional*, defaults to 256): Hidden dimension. min_depth (`float`, *optional*, defaults to 1e-3): Min depth value. max_depth (`float`, *optional*, defaults to 10): Max depth value. """ super().__init__() self.in_features = config.bottleneck_features self.bin_centers_type = config.bin_centers_type self.min_depth = min_depth self.max_depth = max_depth self.conv1 = nn.Conv2d(self.in_features, mlp_dim, 1, 1, 0) self.act1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(mlp_dim, n_bins, 1, 1, 0) self.act2 = nn.ReLU(inplace=True) if self.bin_centers_type == "normed" else nn.Softplus() def forward(self, x): """ Returns tensor of bin_width vectors (centers). One vector b for every pixel """ x = self.conv1(x) x = self.act1(x) x = self.conv2(x) bin_centers = self.act2(x) if self.bin_centers_type == "normed": bin_centers = bin_centers + 1e-3 bin_widths_normed = bin_centers / bin_centers.sum(dim=1, keepdim=True) # shape (batch_size, num_channels, height, width) bin_widths = (self.max_depth - self.min_depth) * bin_widths_normed # pad has the form (left, right, top, bottom, front, back) bin_widths = nn.functional.pad(bin_widths, (0, 0, 0, 0, 1, 0), mode="constant", value=self.min_depth) # shape (batch_size, num_channels, height, width) bin_edges = torch.cumsum(bin_widths, dim=1) bin_centers = 0.5 * (bin_edges[:, :-1, ...] + bin_edges[:, 1:, ...]) return bin_widths_normed, bin_centers else: return bin_centers, bin_centers @torch.jit.script def inv_attractor(dx, alpha: float = 300, gamma: int = 2): """Inverse attractor: dc = dx / (1 + alpha*dx^gamma), where dx = a - c, a = attractor point, c = bin center, dc = shift in bin center This is the default one according to the accompanying paper. Args: dx (`torch.Tensor`): The difference tensor dx = Ai - Cj, where Ai is the attractor point and Cj is the bin center. alpha (`float`, *optional*, defaults to 300): Proportional Attractor strength. Determines the absolute strength. Lower alpha = greater attraction. gamma (`int`, *optional*, defaults to 2): Exponential Attractor strength. Determines the "region of influence" and indirectly number of bin centers affected. Lower gamma = farther reach. Returns: torch.Tensor: Delta shifts - dc; New bin centers = Old bin centers + dc """ return dx.div(1 + alpha * dx.pow(gamma)) class ZoeDepthAttractorLayer(nn.Module): def __init__( self, config, n_bins, n_attractors=16, min_depth=1e-3, max_depth=10, memory_efficient=False, ): """ Attractor layer for bin centers. Bin centers are bounded on the interval (min_depth, max_depth) """ super().__init__() self.alpha = config.attractor_alpha self.gemma = config.attractor_gamma self.kind = config.attractor_kind self.n_attractors = n_attractors self.n_bins = n_bins self.min_depth = min_depth self.max_depth = max_depth self.memory_efficient = memory_efficient # MLP to predict attractor points in_features = mlp_dim = config.bin_embedding_dim self.conv1 = nn.Conv2d(in_features, mlp_dim, 1, 1, 0) self.act1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(mlp_dim, n_attractors * 2, 1, 1, 0) # x2 for linear norm self.act2 = nn.ReLU(inplace=True) def forward(self, x, prev_bin, prev_bin_embedding=None, interpolate=True): """ The forward pass of the attractor layer. This layer predicts the new bin centers based on the previous bin centers and the attractor points (the latter are predicted by the MLP). Args: x (`torch.Tensor` of shape `(batch_size, num_channels, height, width)`): Feature block. prev_bin (`torch.Tensor` of shape `(batch_size, prev_number_of_bins, height, width)`): Previous bin centers normed. prev_bin_embedding (`torch.Tensor`, *optional*): Optional previous bin embeddings. interpolate (`bool`, *optional*, defaults to `True`): Whether to interpolate the previous bin embeddings to the size of the input features. Returns: `Tuple[`torch.Tensor`, `torch.Tensor`]: New bin centers normed and scaled. """ if prev_bin_embedding is not None: if interpolate: prev_bin_embedding = nn.functional.interpolate( prev_bin_embedding, x.shape[-2:], mode="bilinear", align_corners=True ) x = x + prev_bin_embedding x = self.conv1(x) x = self.act1(x) x = self.conv2(x) attractors = self.act2(x) attractors = attractors + 1e-3 batch_size, _, height, width = attractors.shape attractors = attractors.view(batch_size, self.n_attractors, 2, height, width) # batch_size, num_attractors, 2, height, width # note: original repo had a bug here: https://github.com/isl-org/ZoeDepth/blame/edb6daf45458569e24f50250ef1ed08c015f17a7/zoedepth/models/layers/attractor.py#L105C9-L106C50 # we include the bug to maintain compatibility with the weights attractors_normed = attractors[:, :, 0, ...] # batch_size, batch_size*num_attractors, height, width bin_centers = nn.functional.interpolate(prev_bin, (height, width), mode="bilinear", align_corners=True) # note: only attractor_type = "exp" is supported here, since no checkpoints were released with other attractor types if not self.memory_efficient: func = {"mean": torch.mean, "sum": torch.sum}[self.kind] # shape (batch_size, num_bins, height, width) delta_c = func(inv_attractor(attractors_normed.unsqueeze(2) - bin_centers.unsqueeze(1)), dim=1) else: delta_c = torch.zeros_like(bin_centers, device=bin_centers.device) for i in range(self.n_attractors): # shape (batch_size, num_bins, height, width) delta_c += inv_attractor(attractors_normed[:, i, ...].unsqueeze(1) - bin_centers) if self.kind == "mean": delta_c = delta_c / self.n_attractors bin_new_centers = bin_centers + delta_c bin_centers = (self.max_depth - self.min_depth) * bin_new_centers + self.min_depth bin_centers, _ = torch.sort(bin_centers, dim=1) bin_centers = torch.clip(bin_centers, self.min_depth, self.max_depth) return bin_new_centers, bin_centers class ZoeDepthAttractorLayerUnnormed(nn.Module): def __init__( self, config, n_bins, n_attractors=16, min_depth=1e-3, max_depth=10, memory_efficient=True, ): """ Attractor layer for bin centers. Bin centers are unbounded """ super().__init__() self.n_attractors = n_attractors self.n_bins = n_bins self.min_depth = min_depth self.max_depth = max_depth self.alpha = config.attractor_alpha self.gamma = config.attractor_alpha self.kind = config.attractor_kind self.memory_efficient = memory_efficient in_features = mlp_dim = config.bin_embedding_dim self.conv1 = nn.Conv2d(in_features, mlp_dim, 1, 1, 0) self.act1 = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(mlp_dim, n_attractors, 1, 1, 0) self.act2 = nn.Softplus() def forward(self, x, prev_bin, prev_bin_embedding=None, interpolate=True): """ The forward pass of the attractor layer. This layer predicts the new bin centers based on the previous bin centers and the attractor points (the latter are predicted by the MLP). Args: x (`torch.Tensor` of shape (batch_size, num_channels, height, width)`): Feature block. prev_bin (`torch.Tensor` of shape (batch_size, prev_num_bins, height, width)`): Previous bin centers normed. prev_bin_embedding (`torch.Tensor`, *optional*): Optional previous bin embeddings. interpolate (`bool`, *optional*, defaults to `True`): Whether to interpolate the previous bin embeddings to the size of the input features. Returns: `Tuple[`torch.Tensor`, `torch.Tensor`]: New bin centers unbounded. Two outputs just to keep the API consistent with the normed version. """ if prev_bin_embedding is not None: if interpolate: prev_bin_embedding = nn.functional.interpolate( prev_bin_embedding, x.shape[-2:], mode="bilinear", align_corners=True ) x = x + prev_bin_embedding x = self.conv1(x) x = self.act1(x) x = self.conv2(x) attractors = self.act2(x) height, width = attractors.shape[-2:] bin_centers = nn.functional.interpolate(prev_bin, (height, width), mode="bilinear", align_corners=True) if not self.memory_efficient: func = {"mean": torch.mean, "sum": torch.sum}[self.kind] # shape batch_size, num_bins, height, width delta_c = func(inv_attractor(attractors.unsqueeze(2) - bin_centers.unsqueeze(1)), dim=1) else: delta_c = torch.zeros_like(bin_centers, device=bin_centers.device) for i in range(self.n_attractors): # shape batch_size, num_bins, height, width delta_c += inv_attractor(attractors[:, i, ...].unsqueeze(1) - bin_centers) if self.kind == "mean": delta_c = delta_c / self.n_attractors bin_new_centers = bin_centers + delta_c bin_centers = bin_new_centers return bin_new_centers, bin_centers class ZoeDepthProjector(nn.Module): def __init__(self, in_features, out_features, mlp_dim=128): """Projector MLP. Args: in_features (`int`): Number of input channels. out_features (`int`): Number of output channels. mlp_dim (`int`, *optional*, defaults to 128): Hidden dimension. """ super().__init__() self.conv1 = nn.Conv2d(in_features, mlp_dim, 1, 1, 0) self.act = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(mlp_dim, out_features, 1, 1, 0) def forward(self, hidden_state: torch.Tensor) -> torch.Tensor: hidden_state = self.conv1(hidden_state) hidden_state = self.act(hidden_state) hidden_state = self.conv2(hidden_state) return hidden_state # Copied from transformers.models.grounding_dino.modeling_grounding_dino.GroundingDinoMultiheadAttention with GroundingDino->ZoeDepth class ZoeDepthMultiheadAttention(nn.Module): """Equivalent implementation of nn.MultiheadAttention with `batch_first=True`.""" # Ignore copy def __init__(self, hidden_size, num_attention_heads, dropout): super().__init__() if hidden_size % num_attention_heads != 0: raise ValueError( f"The hidden size ({hidden_size}) is not a multiple of the number of attention " f"heads ({num_attention_heads})" ) self.num_attention_heads = num_attention_heads self.attention_head_size = int(hidden_size / num_attention_heads) self.all_head_size = self.num_attention_heads * self.attention_head_size self.query = nn.Linear(hidden_size, self.all_head_size) self.key = nn.Linear(hidden_size, self.all_head_size) self.value = nn.Linear(hidden_size, self.all_head_size) self.out_proj = nn.Linear(hidden_size, hidden_size) self.dropout = nn.Dropout(dropout) def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor: new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) x = x.view(new_x_shape) return x.permute(0, 2, 1, 3) def forward( self, queries: torch.Tensor, keys: torch.Tensor, values: torch.Tensor, attention_mask: Optional[torch.FloatTensor] = None, output_attentions: Optional[bool] = False, ) -> Tuple[torch.Tensor]: query_layer = self.transpose_for_scores(self.query(queries)) key_layer = self.transpose_for_scores(self.key(keys)) value_layer = self.transpose_for_scores(self.value(values)) # Take the dot product between "query" and "key" to get the raw attention scores. attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) attention_scores = attention_scores / math.sqrt(self.attention_head_size) if attention_mask is not None: # Apply the attention mask is (precomputed for all layers in ZoeDepthModel forward() function) attention_scores = attention_scores + attention_mask # Normalize the attention scores to probabilities. attention_probs = nn.functional.softmax(attention_scores, dim=-1) # This is actually dropping out entire tokens to attend to, which might # seem a bit unusual, but is taken from the original Transformer paper. attention_probs = self.dropout(attention_probs) context_layer = torch.matmul(attention_probs, value_layer) context_layer = context_layer.permute(0, 2, 1, 3).contiguous() new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) context_layer = context_layer.view(new_context_layer_shape) context_layer = self.out_proj(context_layer) outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) return outputs class ZoeDepthTransformerEncoderLayer(nn.Module): def __init__(self, config, dropout=0.1, activation="relu"): super().__init__() hidden_size = config.patch_transformer_hidden_size intermediate_size = config.patch_transformer_intermediate_size num_attention_heads = config.patch_transformer_num_attention_heads self.self_attn = ZoeDepthMultiheadAttention(hidden_size, num_attention_heads, dropout=dropout) self.linear1 = nn.Linear(hidden_size, intermediate_size) self.dropout = nn.Dropout(dropout) self.linear2 = nn.Linear(intermediate_size, hidden_size) self.norm1 = nn.LayerNorm(hidden_size) self.norm2 = nn.LayerNorm(hidden_size) self.dropout1 = nn.Dropout(dropout) self.dropout2 = nn.Dropout(dropout) self.activation = ACT2FN[activation] def forward( self, src, src_mask: Optional[torch.Tensor] = None, ): queries = keys = src src2 = self.self_attn(queries=queries, keys=keys, values=src, attention_mask=src_mask)[0] src = src + self.dropout1(src2) src = self.norm1(src) src2 = self.linear2(self.dropout(self.activation(self.linear1(src)))) src = src + self.dropout2(src2) src = self.norm2(src) return src class ZoeDepthPatchTransformerEncoder(nn.Module): def __init__(self, config): """ViT-like transformer block Args: config (`ZoeDepthConfig`): Model configuration class defining the model architecture. """ super().__init__() in_channels = config.bottleneck_features self.transformer_encoder = nn.ModuleList( [ZoeDepthTransformerEncoderLayer(config) for _ in range(config.num_patch_transformer_layers)] ) self.embedding_convPxP = nn.Conv2d( in_channels, config.patch_transformer_hidden_size, kernel_size=1, stride=1, padding=0 ) def positional_encoding_1d(self, batch_size, sequence_length, embedding_dim, device="cpu", dtype=torch.float32): """Generate positional encodings Args: sequence_length (int): Sequence length embedding_dim (int): Embedding dimension Returns: torch.Tensor: Positional encodings. """ position = torch.arange(0, sequence_length, dtype=dtype, device=device).unsqueeze(1) index = torch.arange(0, embedding_dim, 2, dtype=dtype, device=device).unsqueeze(0) div_term = torch.exp(index * (-torch.log(torch.tensor(10000.0, device=device)) / embedding_dim)) pos_encoding = position * div_term pos_encoding = torch.cat([torch.sin(pos_encoding), torch.cos(pos_encoding)], dim=1) pos_encoding = pos_encoding.unsqueeze(dim=0).repeat(batch_size, 1, 1) return pos_encoding def forward(self, x): """Forward pass Args: x (torch.Tensor - NCHW): Input feature tensor Returns: torch.Tensor - Transformer output embeddings of shape (batch_size, sequence_length, embedding_dim) """ embeddings = self.embedding_convPxP(x).flatten(2) # shape (batch_size, num_channels, sequence_length) # add an extra special CLS token at the start for global accumulation embeddings = nn.functional.pad(embeddings, (1, 0)) embeddings = embeddings.permute(0, 2, 1) batch_size, sequence_length, embedding_dim = embeddings.shape embeddings = embeddings + self.positional_encoding_1d( batch_size, sequence_length, embedding_dim, device=embeddings.device, dtype=embeddings.dtype ) for i in range(4): embeddings = self.transformer_encoder[i](embeddings) return embeddings class ZoeDepthMLPClassifier(nn.Module): def __init__(self, in_features, out_features) -> None: super().__init__() hidden_features = in_features self.linear1 = nn.Linear(in_features, hidden_features) self.activation = nn.ReLU() self.linear2 = nn.Linear(hidden_features, out_features) def forward(self, hidden_state): hidden_state = self.linear1(hidden_state) hidden_state = self.activation(hidden_state) domain_logits = self.linear2(hidden_state) return domain_logits class ZoeDepthMultipleMetricDepthEstimationHeads(nn.Module): """ Multiple metric depth estimation heads. A MLP classifier is used to route between 2 different heads. """ def __init__(self, config): super().__init__() bin_embedding_dim = config.bin_embedding_dim n_attractors = config.num_attractors self.bin_configurations = config.bin_configurations self.bin_centers_type = config.bin_centers_type # Bottleneck convolution bottleneck_features = config.bottleneck_features self.conv2 = nn.Conv2d(bottleneck_features, bottleneck_features, kernel_size=1, stride=1, padding=0) # Transformer classifier on the bottleneck self.patch_transformer = ZoeDepthPatchTransformerEncoder(config) # MLP classifier self.mlp_classifier = ZoeDepthMLPClassifier(in_features=128, out_features=2) # Regressor and attractor if self.bin_centers_type == "normed": Attractor = ZoeDepthAttractorLayer elif self.bin_centers_type == "softplus": Attractor = ZoeDepthAttractorLayerUnnormed # We have bins for each bin configuration # Create a map (ModuleDict) of 'name' -> seed_bin_regressor self.seed_bin_regressors = nn.ModuleDict( { conf["name"]: ZoeDepthSeedBinRegressor( config, n_bins=conf["n_bins"], mlp_dim=bin_embedding_dim // 2, min_depth=conf["min_depth"], max_depth=conf["max_depth"], ) for conf in config.bin_configurations } ) self.seed_projector = ZoeDepthProjector( in_features=bottleneck_features, out_features=bin_embedding_dim, mlp_dim=bin_embedding_dim // 2 ) self.projectors = nn.ModuleList( [ ZoeDepthProjector( in_features=config.fusion_hidden_size, out_features=bin_embedding_dim, mlp_dim=bin_embedding_dim // 2, ) for _ in range(4) ] ) # Create a map (ModuleDict) of 'name' -> attractors (ModuleList) self.attractors = nn.ModuleDict( { configuration["name"]: nn.ModuleList( [ Attractor( config, n_bins=n_attractors[i], min_depth=configuration["min_depth"], max_depth=configuration["max_depth"], ) for i in range(len(n_attractors)) ] ) for configuration in config.bin_configurations } ) last_in = config.num_relative_features # conditional log binomial for each bin configuration self.conditional_log_binomial = nn.ModuleDict( { configuration["name"]: ZoeDepthConditionalLogBinomialSoftmax( config, last_in, bin_embedding_dim, configuration["n_bins"], bottleneck_factor=4, ) for configuration in config.bin_configurations } ) def forward(self, outconv_activation, bottleneck, feature_blocks, relative_depth): x = self.conv2(bottleneck) # Predict which path to take # Embedding is of shape (batch_size, hidden_size) embedding = self.patch_transformer(x)[:, 0, :] # MLP classifier to get logits of shape (batch_size, 2) domain_logits = self.mlp_classifier(embedding) domain_vote = torch.softmax(domain_logits.sum(dim=0, keepdim=True), dim=-1) # Get the path names = [configuration["name"] for configuration in self.bin_configurations] bin_configurations_name = names[torch.argmax(domain_vote, dim=-1).squeeze().item()] try: conf = [config for config in self.bin_configurations if config["name"] == bin_configurations_name][0] except IndexError: raise ValueError(f"bin_configurations_name {bin_configurations_name} not found in bin_configurationss") min_depth = conf["min_depth"] max_depth = conf["max_depth"] seed_bin_regressor = self.seed_bin_regressors[bin_configurations_name] _, seed_bin_centers = seed_bin_regressor(x) if self.bin_centers_type in ["normed", "hybrid2"]: prev_bin = (seed_bin_centers - min_depth) / (max_depth - min_depth) else: prev_bin = seed_bin_centers prev_bin_embedding = self.seed_projector(x) attractors = self.attractors[bin_configurations_name] for projector, attractor, feature in zip(self.projectors, attractors, feature_blocks): bin_embedding = projector(feature) bin, bin_centers = attractor(bin_embedding, prev_bin, prev_bin_embedding, interpolate=True) prev_bin = bin prev_bin_embedding = bin_embedding last = outconv_activation bin_centers = nn.functional.interpolate(bin_centers, last.shape[-2:], mode="bilinear", align_corners=True) bin_embedding = nn.functional.interpolate(bin_embedding, last.shape[-2:], mode="bilinear", align_corners=True) conditional_log_binomial = self.conditional_log_binomial[bin_configurations_name] x = conditional_log_binomial(last, bin_embedding) # Now depth value is Sum px * cx , where cx are bin_centers from the last bin tensor out = torch.sum(x * bin_centers, dim=1, keepdim=True) return out, domain_logits class ZoeDepthMetricDepthEstimationHead(nn.Module): def __init__(self, config): super().__init__() bin_configuration = config.bin_configurations[0] n_bins = bin_configuration["n_bins"] min_depth = bin_configuration["min_depth"] max_depth = bin_configuration["max_depth"] bin_embedding_dim = config.bin_embedding_dim n_attractors = config.num_attractors bin_centers_type = config.bin_centers_type self.min_depth = min_depth self.max_depth = max_depth self.bin_centers_type = bin_centers_type # Bottleneck convolution bottleneck_features = config.bottleneck_features self.conv2 = nn.Conv2d(bottleneck_features, bottleneck_features, kernel_size=1, stride=1, padding=0) # Regressor and attractor if self.bin_centers_type == "normed": Attractor = ZoeDepthAttractorLayer elif self.bin_centers_type == "softplus": Attractor = ZoeDepthAttractorLayerUnnormed self.seed_bin_regressor = ZoeDepthSeedBinRegressor( config, n_bins=n_bins, min_depth=min_depth, max_depth=max_depth ) self.seed_projector = ZoeDepthProjector(in_features=bottleneck_features, out_features=bin_embedding_dim) self.projectors = nn.ModuleList( [ ZoeDepthProjector(in_features=config.fusion_hidden_size, out_features=bin_embedding_dim) for _ in range(4) ] ) self.attractors = nn.ModuleList( [ Attractor( config, n_bins=n_bins, n_attractors=n_attractors[i], min_depth=min_depth, max_depth=max_depth, ) for i in range(4) ] ) last_in = config.num_relative_features + 1 # +1 for relative depth # use log binomial instead of softmax self.conditional_log_binomial = ZoeDepthConditionalLogBinomialSoftmax( config, last_in, bin_embedding_dim, n_classes=n_bins, ) def forward(self, outconv_activation, bottleneck, feature_blocks, relative_depth): x = self.conv2(bottleneck) _, seed_bin_centers = self.seed_bin_regressor(x) if self.bin_centers_type in ["normed", "hybrid2"]: prev_bin = (seed_bin_centers - self.min_depth) / (self.max_depth - self.min_depth) else: prev_bin = seed_bin_centers prev_bin_embedding = self.seed_projector(x) # unroll this loop for better performance for projector, attractor, feature in zip(self.projectors, self.attractors, feature_blocks): bin_embedding = projector(feature) bin, bin_centers = attractor(bin_embedding, prev_bin, prev_bin_embedding, interpolate=True) prev_bin = bin.clone() prev_bin_embedding = bin_embedding.clone() last = outconv_activation # concatenative relative depth with last. First interpolate relative depth to last size relative_conditioning = relative_depth.unsqueeze(1) relative_conditioning = nn.functional.interpolate( relative_conditioning, size=last.shape[2:], mode="bilinear", align_corners=True ) last = torch.cat([last, relative_conditioning], dim=1) bin_embedding = nn.functional.interpolate(bin_embedding, last.shape[-2:], mode="bilinear", align_corners=True) x = self.conditional_log_binomial(last, bin_embedding) # Now depth value is Sum px * cx , where cx are bin_centers from the last bin tensor bin_centers = nn.functional.interpolate(bin_centers, x.shape[-2:], mode="bilinear", align_corners=True) out = torch.sum(x * bin_centers, dim=1, keepdim=True) return out, None # Copied from transformers.models.dpt.modeling_dpt.DPTPreTrainedModel with DPT->ZoeDepth,dpt->zoedepth class ZoeDepthPreTrainedModel(PreTrainedModel): """ An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained models. """ config_class = ZoeDepthConfig base_model_prefix = "zoedepth" main_input_name = "pixel_values" supports_gradient_checkpointing = True def _init_weights(self, module): """Initialize the weights""" if isinstance(module, (nn.Linear, nn.Conv2d, nn.ConvTranspose2d)): # Slightly different from the TF version which uses truncated_normal for initialization # cf https://github.com/pytorch/pytorch/pull/5617 module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) if module.bias is not None: module.bias.data.zero_() elif isinstance(module, nn.LayerNorm): module.bias.data.zero_() module.weight.data.fill_(1.0) ZOEDEPTH_START_DOCSTRING = r""" This model is a PyTorch [torch.nn.Module](https://pytorch.org/docs/stable/nn.html#torch.nn.Module) subclass. Use it as a regular PyTorch Module and refer to the PyTorch documentation for all matter related to general usage and behavior. Parameters: config ([`ViTConfig`]): Model configuration class with all the parameters of the model. Initializing with a config file does not load the weights associated with the model, only the configuration. Check out the [`~PreTrainedModel.from_pretrained`] method to load the model weights. """ ZOEDEPTH_INPUTS_DOCSTRING = r""" Args: pixel_values (`torch.FloatTensor` of shape `(batch_size, num_channels, height, width)`): Pixel values. Pixel values can be obtained using [`AutoImageProcessor`]. See [`DPTImageProcessor.__call__`] for details. output_attentions (`bool`, *optional*): Whether or not to return the attentions tensors of all attention layers. See `attentions` under returned tensors for more detail. output_hidden_states (`bool`, *optional*): Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors for more detail. return_dict (`bool`, *optional*): Whether or not to return a [`~file_utils.ModelOutput`] instead of a plain tuple. """ @add_start_docstrings( """ ZoeDepth model with one or multiple metric depth estimation head(s) on top. """, ZOEDEPTH_START_DOCSTRING, ) class ZoeDepthForDepthEstimation(ZoeDepthPreTrainedModel): def __init__(self, config): super().__init__(config) self.backbone = load_backbone(config) if hasattr(self.backbone.config, "hidden_size") and hasattr(self.backbone.config, "patch_size"): config.backbone_hidden_size = self.backbone.config.hidden_size self.patch_size = self.backbone.config.patch_size else: raise ValueError( "ZoeDepth assumes the backbone's config to have `hidden_size` and `patch_size` attributes" ) self.neck = ZoeDepthNeck(config) self.relative_head = ZoeDepthRelativeDepthEstimationHead(config) self.metric_head = ( ZoeDepthMultipleMetricDepthEstimationHeads(config) if len(config.bin_configurations) > 1 else ZoeDepthMetricDepthEstimationHead(config) ) # Initialize weights and apply final processing self.post_init() @add_start_docstrings_to_model_forward(ZOEDEPTH_INPUTS_DOCSTRING) @replace_return_docstrings(output_type=DepthEstimatorOutput, config_class=_CONFIG_FOR_DOC) def forward( self, pixel_values: torch.FloatTensor, labels: Optional[torch.LongTensor] = None, output_attentions: Optional[bool] = None, output_hidden_states: Optional[bool] = None, return_dict: Optional[bool] = None, ) -> Union[Tuple[torch.Tensor], DepthEstimatorOutput]: r""" labels (`torch.LongTensor` of shape `(batch_size, height, width)`, *optional*): Ground truth depth estimation maps for computing the loss. Returns: Examples: ```python >>> from transformers import AutoImageProcessor, ZoeDepthForDepthEstimation >>> import torch >>> import numpy as np >>> from PIL import Image >>> import requests >>> url = "http://images.cocodataset.org/val2017/000000039769.jpg" >>> image = Image.open(requests.get(url, stream=True).raw) >>> image_processor = AutoImageProcessor.from_pretrained("Intel/zoedepth-nyu-kitti") >>> model = ZoeDepthForDepthEstimation.from_pretrained("Intel/zoedepth-nyu-kitti") >>> # prepare image for the model >>> inputs = image_processor(images=image, return_tensors="pt") >>> with torch.no_grad(): ... outputs = model(**inputs) ... predicted_depth = outputs.predicted_depth >>> # interpolate to original size >>> prediction = torch.nn.functional.interpolate( ... predicted_depth.unsqueeze(1), ... size=image.size[::-1], ... mode="bicubic", ... align_corners=False, ... ) >>> # visualize the prediction >>> output = prediction.squeeze().cpu().numpy() >>> formatted = (output * 255 / np.max(output)).astype("uint8") >>> depth = Image.fromarray(formatted) ```""" loss = None if labels is not None: raise NotImplementedError("Training is not implemented yet") return_dict = return_dict if return_dict is not None else self.config.use_return_dict output_hidden_states = ( output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states ) output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions outputs = self.backbone.forward_with_filtered_kwargs( pixel_values, output_hidden_states=output_hidden_states, output_attentions=output_attentions ) hidden_states = outputs.feature_maps _, _, height, width = pixel_values.shape patch_size = self.patch_size patch_height = height // patch_size patch_width = width // patch_size hidden_states, features = self.neck(hidden_states, patch_height, patch_width) out = [features] + hidden_states relative_depth, features = self.relative_head(hidden_states) out = [features] + out metric_depth, domain_logits = self.metric_head( outconv_activation=out[0], bottleneck=out[1], feature_blocks=out[2:], relative_depth=relative_depth ) metric_depth = metric_depth.squeeze(dim=1) if not return_dict: if domain_logits is not None: output = (metric_depth, domain_logits) + outputs[1:] else: output = (metric_depth,) + outputs[1:] return ((loss,) + output) if loss is not None else output return ZoeDepthDepthEstimatorOutput( loss=loss, predicted_depth=metric_depth, domain_logits=domain_logits, hidden_states=outputs.hidden_states, attentions=outputs.attentions, )