| | |
| | import math |
| | from typing import Any, List |
| | import torch |
| | from torch import nn |
| | from torch.nn import functional as F |
| |
|
| | from detectron2.config import CfgNode |
| | from detectron2.structures import Instances |
| |
|
| | from .. import DensePoseConfidenceModelConfig, DensePoseUVConfidenceType |
| | from .chart import DensePoseChartLoss |
| | from .registry import DENSEPOSE_LOSS_REGISTRY |
| | from .utils import BilinearInterpolationHelper, LossDict |
| |
|
| |
|
| | @DENSEPOSE_LOSS_REGISTRY.register() |
| | class DensePoseChartWithConfidenceLoss(DensePoseChartLoss): |
| | """ """ |
| |
|
| | def __init__(self, cfg: CfgNode): |
| | super().__init__(cfg) |
| | self.confidence_model_cfg = DensePoseConfidenceModelConfig.from_cfg(cfg) |
| | if self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.IID_ISO: |
| | self.uv_loss_with_confidences = IIDIsotropicGaussianUVLoss( |
| | self.confidence_model_cfg.uv_confidence.epsilon |
| | ) |
| | elif self.confidence_model_cfg.uv_confidence.type == DensePoseUVConfidenceType.INDEP_ANISO: |
| | self.uv_loss_with_confidences = IndepAnisotropicGaussianUVLoss( |
| | self.confidence_model_cfg.uv_confidence.epsilon |
| | ) |
| |
|
| | def produce_fake_densepose_losses_uv(self, densepose_predictor_outputs: Any) -> LossDict: |
| | """ |
| | Overrides fake losses for fine segmentation and U/V coordinates to |
| | include computation graphs for additional confidence parameters. |
| | These are used when no suitable ground truth data was found in a batch. |
| | The loss has a value 0 and is primarily used to construct the computation graph, |
| | so that `DistributedDataParallel` has similar graphs on all GPUs and can |
| | perform reduction properly. |
| | |
| | Args: |
| | densepose_predictor_outputs: DensePose predictor outputs, an object |
| | of a dataclass that is assumed to have the following attributes: |
| | * fine_segm - fine segmentation estimates, tensor of shape [N, C, S, S] |
| | * u - U coordinate estimates per fine labels, tensor of shape [N, C, S, S] |
| | * v - V coordinate estimates per fine labels, tensor of shape [N, C, S, S] |
| | Return: |
| | dict: str -> tensor: dict of losses with the following entries: |
| | * `loss_densepose_U`: has value 0 |
| | * `loss_densepose_V`: has value 0 |
| | * `loss_densepose_I`: has value 0 |
| | """ |
| | conf_type = self.confidence_model_cfg.uv_confidence.type |
| | if self.confidence_model_cfg.uv_confidence.enabled: |
| | loss_uv = ( |
| | densepose_predictor_outputs.u.sum() + densepose_predictor_outputs.v.sum() |
| | ) * 0 |
| | if conf_type == DensePoseUVConfidenceType.IID_ISO: |
| | loss_uv += densepose_predictor_outputs.sigma_2.sum() * 0 |
| | elif conf_type == DensePoseUVConfidenceType.INDEP_ANISO: |
| | loss_uv += ( |
| | densepose_predictor_outputs.sigma_2.sum() |
| | + densepose_predictor_outputs.kappa_u.sum() |
| | + densepose_predictor_outputs.kappa_v.sum() |
| | ) * 0 |
| | return {"loss_densepose_UV": loss_uv} |
| | else: |
| | return super().produce_fake_densepose_losses_uv(densepose_predictor_outputs) |
| |
|
| | def produce_densepose_losses_uv( |
| | self, |
| | proposals_with_gt: List[Instances], |
| | densepose_predictor_outputs: Any, |
| | packed_annotations: Any, |
| | interpolator: BilinearInterpolationHelper, |
| | j_valid_fg: torch.Tensor, |
| | ) -> LossDict: |
| | conf_type = self.confidence_model_cfg.uv_confidence.type |
| | if self.confidence_model_cfg.uv_confidence.enabled: |
| | u_gt = packed_annotations.u_gt[j_valid_fg] |
| | u_est = interpolator.extract_at_points(densepose_predictor_outputs.u)[j_valid_fg] |
| | v_gt = packed_annotations.v_gt[j_valid_fg] |
| | v_est = interpolator.extract_at_points(densepose_predictor_outputs.v)[j_valid_fg] |
| | sigma_2_est = interpolator.extract_at_points(densepose_predictor_outputs.sigma_2)[ |
| | j_valid_fg |
| | ] |
| | if conf_type == DensePoseUVConfidenceType.IID_ISO: |
| | return { |
| | "loss_densepose_UV": ( |
| | self.uv_loss_with_confidences(u_est, v_est, sigma_2_est, u_gt, v_gt) |
| | * self.w_points |
| | ) |
| | } |
| | elif conf_type in [DensePoseUVConfidenceType.INDEP_ANISO]: |
| | kappa_u_est = interpolator.extract_at_points(densepose_predictor_outputs.kappa_u)[ |
| | j_valid_fg |
| | ] |
| | kappa_v_est = interpolator.extract_at_points(densepose_predictor_outputs.kappa_v)[ |
| | j_valid_fg |
| | ] |
| | return { |
| | "loss_densepose_UV": ( |
| | self.uv_loss_with_confidences( |
| | u_est, v_est, sigma_2_est, kappa_u_est, kappa_v_est, u_gt, v_gt |
| | ) |
| | * self.w_points |
| | ) |
| | } |
| | return super().produce_densepose_losses_uv( |
| | proposals_with_gt, |
| | densepose_predictor_outputs, |
| | packed_annotations, |
| | interpolator, |
| | j_valid_fg, |
| | ) |
| |
|
| |
|
| | class IIDIsotropicGaussianUVLoss(nn.Module): |
| | """ |
| | Loss for the case of iid residuals with isotropic covariance: |
| | $Sigma_i = sigma_i^2 I$ |
| | The loss (negative log likelihood) is then: |
| | $1/2 sum_{i=1}^n (log(2 pi) + 2 log sigma_i^2 + ||delta_i||^2 / sigma_i^2)$, |
| | where $delta_i=(u - u', v - v')$ is a 2D vector containing UV coordinates |
| | difference between estimated and ground truth UV values |
| | For details, see: |
| | N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning |
| | Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 |
| | """ |
| |
|
| | def __init__(self, sigma_lower_bound: float): |
| | super(IIDIsotropicGaussianUVLoss, self).__init__() |
| | self.sigma_lower_bound = sigma_lower_bound |
| | self.log2pi = math.log(2 * math.pi) |
| |
|
| | def forward( |
| | self, |
| | u: torch.Tensor, |
| | v: torch.Tensor, |
| | sigma_u: torch.Tensor, |
| | target_u: torch.Tensor, |
| | target_v: torch.Tensor, |
| | ): |
| | |
| | |
| | |
| | sigma2 = F.softplus(sigma_u) + self.sigma_lower_bound |
| | |
| | |
| | delta_t_delta = (u - target_u) ** 2 + (v - target_v) ** 2 |
| | |
| | loss = 0.5 * (self.log2pi + 2 * torch.log(sigma2) + delta_t_delta / sigma2) |
| | return loss.sum() |
| |
|
| |
|
| | class IndepAnisotropicGaussianUVLoss(nn.Module): |
| | """ |
| | Loss for the case of independent residuals with anisotropic covariances: |
| | $Sigma_i = sigma_i^2 I + r_i r_i^T$ |
| | The loss (negative log likelihood) is then: |
| | $1/2 sum_{i=1}^n (log(2 pi) |
| | + log sigma_i^2 (sigma_i^2 + ||r_i||^2) |
| | + ||delta_i||^2 / sigma_i^2 |
| | - <delta_i, r_i>^2 / (sigma_i^2 * (sigma_i^2 + ||r_i||^2)))$, |
| | where $delta_i=(u - u', v - v')$ is a 2D vector containing UV coordinates |
| | difference between estimated and ground truth UV values |
| | For details, see: |
| | N. Neverova, D. Novotny, A. Vedaldi "Correlated Uncertainty for Learning |
| | Dense Correspondences from Noisy Labels", p. 918--926, in Proc. NIPS 2019 |
| | """ |
| |
|
| | def __init__(self, sigma_lower_bound: float): |
| | super(IndepAnisotropicGaussianUVLoss, self).__init__() |
| | self.sigma_lower_bound = sigma_lower_bound |
| | self.log2pi = math.log(2 * math.pi) |
| |
|
| | def forward( |
| | self, |
| | u: torch.Tensor, |
| | v: torch.Tensor, |
| | sigma_u: torch.Tensor, |
| | kappa_u_est: torch.Tensor, |
| | kappa_v_est: torch.Tensor, |
| | target_u: torch.Tensor, |
| | target_v: torch.Tensor, |
| | ): |
| | |
| | sigma2 = F.softplus(sigma_u) + self.sigma_lower_bound |
| | |
| | |
| | r_sqnorm2 = kappa_u_est**2 + kappa_v_est**2 |
| | delta_u = u - target_u |
| | delta_v = v - target_v |
| | |
| | |
| | delta_sqnorm = delta_u**2 + delta_v**2 |
| | delta_u_r_u = delta_u * kappa_u_est |
| | delta_v_r_v = delta_v * kappa_v_est |
| | |
| | delta_r = delta_u_r_u + delta_v_r_v |
| | |
| | |
| | delta_r_sqnorm = delta_r**2 |
| | denom2 = sigma2 * (sigma2 + r_sqnorm2) |
| | loss = 0.5 * ( |
| | self.log2pi + torch.log(denom2) + delta_sqnorm / sigma2 - delta_r_sqnorm / denom2 |
| | ) |
| | return loss.sum() |
| |
|