class_docstr: Module, which performs optimization-based image registration.
  
  Args:
      model_type: Geometrical model for registration. Can be string or Module.
      optimizer: optimizer class used for the optimization.
      loss_fn: torch loss function.
      pyramid_levels: number of scale pyramid levels.
      lr: learning rate for optimization.
      num_iterations: maximum number of iterations.
      tolerance: stop optimizing if loss difference is less. default 1e-4.
      warper: if model_type is not string, one needs to provide warper object.
  
  Example:
      >>> from kornia.geometry import ImageRegistrator
      >>> img_src = torch.rand(1, 1, 32, 32)
      >>> img_dst = torch.rand(1, 1, 32, 32)
      >>> registrator = ImageRegistrator('similarity')
      >>> homo = registrator.register(img_src, img_dst)
docstr: Estimate the tranformation' which warps src_img into dst_img by gradient descent. The shape of the
  tensors is not checked, because it may depend on the model, e.g. volume registration.
  
  Args:
      src_img: Input image tensor.
      dst_img: Input image tensor.
      verbose: if True, outputs loss every 10 iterations.
      output_intermediate_models: if True with intermediate models
  
  Returns:
      the transformation between two images, shape depends on the model,
      typically [1x3x3] tensor for string model_types.
docstr_tok: ["Estimate","the","tranformation","'","which","warps","src_img","into","dst_img","by","gradient","descent",".","The","shape","of","the","tensors","is","not","checked",",","because","it","may","depend","on","the","model",",","e.g",".","volume","registration",".","Args",":","src_img",":","Input","image","tensor",".","dst_img",":","Input","image","tensor",".","verbose",":","if","True",",","outputs","loss","every","10","iterations",".","output_intermediate_models",":","if","True","with","intermediate","models","Returns",":","the","transformation","between","two","images",",","shape","depends","on","the","model",",","typically","[","1x3x3","]","tensor","for","string","model_types","."]
code: def register(
      self,
      src_img: Tensor,
      dst_img: Tensor,
      verbose: bool = False,
      output_intermediate_models: bool = False,
  ) -> Union[Tensor, Tuple[Tensor, List[Tensor]]]:
      r"""Estimate the tranformation' which warps src_img into dst_img by gradient descent. The shape of the
      tensors is not checked, because it may depend on the model, e.g. volume registration.
  
      Args:
          src_img: Input image tensor.
          dst_img: Input image tensor.
          verbose: if True, outputs loss every 10 iterations.
          output_intermediate_models: if True with intermediate models
  
      Returns:
          the transformation between two images, shape depends on the model,
          typically [1x3x3] tensor for string model_types.
      """
      self.reset_model()
      # ToDo: better parameter passing to optimizer
      _opt_args: Dict[str, Any] = {}
      _opt_args["lr"] = self.lr
      opt = self.optimizer(self.model.parameters(), **_opt_args)
  
      # compute the gaussian pyramids
      # [::-1] because we have to register from coarse to fine
      img_src_pyr = build_pyramid(src_img, self.pyramid_levels)[::-1]
      img_dst_pyr = build_pyramid(dst_img, self.pyramid_levels)[::-1]
      prev_loss = 1e10
      aux_models = []
      if len(img_dst_pyr) != len(img_src_pyr):
          raise ValueError("Cannot register images of different sizes")
      for img_src_level, img_dst_level in zip(img_src_pyr, img_dst_pyr):
          for i in range(self.num_iterations):
              # compute gradient and update optimizer parameters
              opt.zero_grad()
              loss = self.get_single_level_loss(
                  img_src_level, img_dst_level, self.model()
              )
              loss += self.get_single_level_loss(
                  img_dst_level,
                  img_src_level,
                  self.model.forward_inverse(),
              )
              current_loss = loss.item()
              if abs(current_loss - prev_loss) < self.tolerance:
                  break
              prev_loss = current_loss
              loss.backward()
              if verbose and (i % 10 == 0):
                  print(f"Loss = {current_loss:.4f}, iter={i}")
              opt.step()
          if output_intermediate_models:
              aux_models.append(self.model().clone().detach())
      if output_intermediate_models:
          return self.model(), aux_models
      return self.model()
  
imports: from abc import abstractmethod
  from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union
  import torch
  import torch.nn.functional
  from torch import nn, optim
  from kornia.core import Module, Tensor
  from kornia.geometry.conversions import angle_to_rotation_matrix, convert_affinematrix_to_homography
  from .homography_warper import BaseWarper, HomographyWarper
  from .pyramid import build_pyramid
label_desc: Use image node_id 4 for calling the ImageRegistrator obj's underlying member method code with example usage: obj.register(src_img, dst_img, verbose, output_intermediate_models) and returns: self, self,  aux_models