class_docstr: FineMatching with s2d paradigm.
class_docstr_tok: ["FineMatching","with","s2d","paradigm","."]
docstr: Args:
      feat0 (torch.Tensor): [M, WW, C]
      feat1 (torch.Tensor): [M, WW, C]
      data (dict)
  Update:
      data (dict):{
          'expec_f' (torch.Tensor): [M, 3],
          'mkpts0_f' (torch.Tensor): [M, 2],
          'mkpts1_f' (torch.Tensor): [M, 2]}
docstr_tok: ["Args",":","feat0","(","torch.Tensor",")",":","[","M",",","WW",",","C","]","feat1","(","torch.Tensor",")",":","[","M",",","WW",",","C","]","data","(","dict",")","Update",":","data","(","dict",")",":","{","'expec_f","'","(","torch.Tensor",")",":","[","M",",","3","]",",","'mkpts0_f","'","(","torch.Tensor",")",":","[","M",",","2","]",",","'mkpts1_f","'","(","torch.Tensor",")",":","[","M",",","2","]","}"]
code: def forward(
      self, feat_f0: Tensor, feat_f1: Tensor, data: dict[str, Any]
  ) -> None:
      """
      Args:
          feat0 (torch.Tensor): [M, WW, C]
          feat1 (torch.Tensor): [M, WW, C]
          data (dict)
      Update:
          data (dict):{
              'expec_f' (torch.Tensor): [M, 3],
              'mkpts0_f' (torch.Tensor): [M, 2],
              'mkpts1_f' (torch.Tensor): [M, 2]}
      """
      M, WW, C = feat_f0.shape
      W = int(math.sqrt(WW))
      scale = data["hw0_i"][0] / data["hw0_f"][0]
      self.M, self.W, self.WW, self.C, self.scale = M, W, WW, C, scale
  
      # corner case: if no coarse matches found
      if M == 0:
          if self.training:
              raise ValueError(
                  "M >0, when training, see coarse_matching.py"
              )
          # logger.warning('No matches found in coarse-level.')
          data.update(
              {
                  "expec_f": torch.empty(
                      0, 3, device=feat_f0.device, dtype=feat_f0.dtype
                  ),
                  "mkpts0_f": data["mkpts0_c"],
                  "mkpts1_f": data["mkpts1_c"],
              }
          )
          return
  
      feat_f0_picked = feat_f0[:, WW // 2, :]
      sim_matrix = torch.einsum("mc,mrc->mr", feat_f0_picked, feat_f1)
      softmax_temp = 1.0 / C**0.5
      heatmap = torch.softmax(softmax_temp * sim_matrix, dim=1).view(
          -1, W, W
      )
  
      # compute coordinates from heatmap
      coords_normalized = dsnt.spatial_expectation2d(
          heatmap[None], True
      )[
          0
      ]  # [M, 2]
      grid_normalized = create_meshgrid(
          W,
          W,
          normalized_coordinates=True,
          device=heatmap.device,
          dtype=heatmap.dtype,
      ).reshape(
          1, -1, 2
      )  # [1, WW, 2]
  
      # compute std over <x, y>
      var = (
          torch.sum(
              grid_normalized**2 * heatmap.view(-1, WW, 1), dim=1
          )
          - coords_normalized**2
      )  # [M, 2]
      std = torch.sum(
          torch.sqrt(torch.clamp(var, min=1e-10)), -1
      )  # [M]  clamp needed for numerical stability
  
      # for fine-level supervision
      data.update(
          {
              "expec_f": torch.cat(
                  [coords_normalized, std.unsqueeze(1)], -1
              )
          }
      )
  
      # compute absolute kpt coords
      self.get_fine_match(coords_normalized, data)
  
file_path: kornia/kornia/feature/loftr/utils/fine_matching.py
imports: from __future__ import annotations
  import math
  from typing import Any
  import torch
  from kornia.core import Module, Tensor
  from kornia.geometry.subpix import dsnt
  from kornia.utils.grid import create_meshgrid
label_desc: Use image node_id 2 for calling the FineMatching obj's underlying member method code with example usage: obj.forward(feat_f0, feat_f1, data) without return types