docstr: Args:
      feat0 (torch.Tensor): [N, L, C]
      feat1 (torch.Tensor): [N, S, C]
      data (dict)
      mask_c0 (torch.Tensor): [N, L] (optional)
      mask_c1 (torch.Tensor): [N, S] (optional)
  Update:
      data (dict): {
          'b_ids' (torch.Tensor): [M'],
          'i_ids' (torch.Tensor): [M'],
          'j_ids' (torch.Tensor): [M'],
          'gt_mask' (torch.Tensor): [M'],
          'mkpts0_c' (torch.Tensor): [M, 2],
          'mkpts1_c' (torch.Tensor): [M, 2],
          'mconf' (torch.Tensor): [M]}
      NOTE: M' != M during training.
docstr_tok: ["Args",":","feat0","(","torch.Tensor",")",":","[","N",",","L",",","C","]","feat1","(","torch.Tensor",")",":","[","N",",","S",",","C","]","data","(","dict",")","mask_c0","(","torch.Tensor",")",":","[","N",",","L","]","(","optional",")","mask_c1","(","torch.Tensor",")",":","[","N",",","S","]","(","optional",")","Update",":","data","(","dict",")",":","{","'b_ids","'","(","torch.Tensor",")",":","[","M","'","]",",","'i_ids","'","(","torch.Tensor",")",":","[","M","'","]",",","'j_ids","'","(","torch.Tensor",")",":","[","M","'","]",",","'gt_mask","'","(","torch.Tensor",")",":","[","M","'","]",",","'mkpts0_c","'","(","torch.Tensor",")",":","[","M",",","2","]",",","'mkpts1_c","'","(","torch.Tensor",")",":","[","M",",","2","]",",","'mconf","'","(","torch.Tensor",")",":","[","M","]","}","NOTE",":","M","'","!","=","M","during","training","."]
code: def forward(
      self,
      feat_c0: Tensor,
      feat_c1: Tensor,
      data: Dict[str, Tensor],
      mask_c0: Optional[Tensor] = None,
      mask_c1: Optional[Tensor] = None,
  ) -> None:
      """
      Args:
          feat0 (torch.Tensor): [N, L, C]
          feat1 (torch.Tensor): [N, S, C]
          data (dict)
          mask_c0 (torch.Tensor): [N, L] (optional)
          mask_c1 (torch.Tensor): [N, S] (optional)
      Update:
          data (dict): {
              'b_ids' (torch.Tensor): [M'],
              'i_ids' (torch.Tensor): [M'],
              'j_ids' (torch.Tensor): [M'],
              'gt_mask' (torch.Tensor): [M'],
              'mkpts0_c' (torch.Tensor): [M, 2],
              'mkpts1_c' (torch.Tensor): [M, 2],
              'mconf' (torch.Tensor): [M]}
          NOTE: M' != M during training.
      """
      _, L, S, _ = (
          feat_c0.size(0),
          feat_c0.size(1),
          feat_c1.size(1),
          feat_c0.size(2),
      )
  
      # normalize
      feat_c0, feat_c1 = (
          feat / feat.shape[-1] ** 0.5 for feat in [feat_c0, feat_c1]
      )
  
      if self.match_type == "dual_softmax":
          sim_matrix = (
              torch.einsum("nlc,nsc->nls", feat_c0, feat_c1)
              / self.temperature
          )
          if mask_c0 is not None and mask_c1 is not None:
              sim_matrix.masked_fill_(
                  ~(mask_c0[..., None] * mask_c1[:, None]).bool(), -INF
              )
          conf_matrix = F.softmax(sim_matrix, 1) * F.softmax(
              sim_matrix, 2
          )
  
      elif self.match_type == "sinkhorn":
          # sinkhorn, dustbin included
          sim_matrix = torch.einsum("nlc,nsc->nls", feat_c0, feat_c1)
          if mask_c0 is not None and mask_c1 is not None:
              sim_matrix[:, :L, :S].masked_fill_(
                  ~(mask_c0[..., None] * mask_c1[:, None]).bool(), -INF
              )
  
          # build uniform prior & use sinkhorn
          log_assign_matrix = self.log_optimal_transport(
              sim_matrix, self.bin_score, self.skh_iters
          )
          assign_matrix = log_assign_matrix.exp()
          conf_matrix = assign_matrix[:, :-1, :-1]
  
          # filter prediction with dustbin score (only in evaluation mode)
          if not self.training and self.skh_prefilter:
              filter0 = (assign_matrix.max(dim=2)[1] == S)[
                  :, :-1
              ]  # [N, L]
              filter1 = (assign_matrix.max(dim=1)[1] == L)[
                  :, :-1
              ]  # [N, S]
              conf_matrix[filter0[..., None].repeat(1, 1, S)] = 0
              conf_matrix[filter1[:, None].repeat(1, L, 1)] = 0
  
          if self.config["sparse_spvs"]:
              data.update(
                  {"conf_matrix_with_bin": assign_matrix.clone()}
              )
  
      data.update({"conf_matrix": conf_matrix})
  
      # predict coarse matches from conf_matrix
      data.update(**self.get_coarse_match(conf_matrix, data))
  
file_path: kornia/kornia/feature/loftr/utils/coarse_matching.py
imports: from typing import Any, Dict, Optional, Union
  import torch
  import torch.nn.functional
  from torch import nn
  from kornia.core import Module, Tensor
label_desc: Use image node_id 2 for calling the CoarseMatching obj's underlying member method code with example usage: obj.forward(feat_c0, feat_c1, data, mask_c0, mask_c1) without return types