class_docstr: Create an scale pyramid of image, usually used for local feature detection.
  
  Images are consequently smoothed with Gaussian blur and downscaled.
  
  Args:
      n_levels: number of the levels in octave.
      init_sigma: initial blur level.
      min_size: the minimum size of the octave in pixels.
      double_image: add 2x upscaled image as 1st level of pyramid. OpenCV SIFT does this.
  
  Returns:
      1st output: images
      2nd output: sigmas (coefficients for scale conversion)
      3rd output: pixelDists (coefficients for coordinate conversion)
  
  Shape:
      - Input: :math:`(B, C, H, W)`
      - Output 1st: :math:`[(B, C, NL, H, W), (B, C, NL, H/2, W/2), ...]`
      - Output 2nd: :math:`[(B, NL), (B, NL), (B, NL), ...]`
      - Output 3rd: :math:`[(B, NL), (B, NL), (B, NL), ...]`
  
  Examples:
      >>> input = torch.rand(2, 4, 100, 100)
      >>> sp, sigmas, pds = ScalePyramid(3, 15)(input)
class_docstr_tok: ["Create","an","scale","pyramid","of","image",",","usually","used","for","local","feature","detection",".","Images","are","consequently","smoothed","with","Gaussian","blur","and","downscaled",".","Args",":","n_levels",":","number","of","the","levels","in","octave",".","init_sigma",":","initial","blur","level",".","min_size",":","the","minimum","size","of","the","octave","in","pixels",".","double_image",":","add","2x","upscaled","image","as","1st","level","of","pyramid",".","OpenCV","SIFT","does","this",".","Returns",":","1st","output",":","images","2nd","output",":","sigmas","(","coefficients","for","scale","conversion",")","3rd","output",":","pixelDists","(","coefficients","for","coordinate","conversion",")","Shape",":","-","Input",":",":","math",":","`","(","B",",","C",",","H",",","W",")","`","-","Output","1st",":",":","math",":","`","[","(","B",",","C",",","NL",",","H",",","W",")",",","(","B",",","C",",","NL",",","H\/2",",","W\/2",")",",","...","]","`","-","Output","2nd",":",":","math",":","`","[","(","B",",","NL",")",",","(","B",",","NL",")",",","(","B",",","NL",")",",","...","]","`","-","Output","3rd",":",":","math",":","`","[","(","B",",","NL",")",",","(","B",",","NL",")",",","(","B",",","NL",")",",","...","]","`","Examples",":",">",">",">","input","=","torch.rand","(","2",",","4",",","100",",","100",")",">",">",">","sp",",","sigmas",",","pds","=","ScalePyramid","(","3",",","15",")","(","input",")"]
code: def forward(
      self, x: Tensor
  ) -> tuple[list[Tensor], list[Tensor], list[Tensor]]:
      bs, _, _, _ = x.size()
      cur_level, cur_sigma, pixel_distance = self.get_first_level(x)
  
      sigmas = [
          cur_sigma
          * ones(bs, self.n_levels + self.extra_levels)
          .to(x.device)
          .to(x.dtype)
      ]
      pixel_dists = [
          pixel_distance
          * ones(bs, self.n_levels + self.extra_levels)
          .to(x.device)
          .to(x.dtype)
      ]
      pyr = [[cur_level]]
      oct_idx = 0
      while True:
          cur_level = pyr[-1][0]
          for level_idx in range(1, self.n_levels + self.extra_levels):
              sigma = cur_sigma * math.sqrt(self.sigma_step**2 - 1.0)
              ksize = self.get_kernel_size(sigma)
  
              # Hack, because PyTorch does not allow to pad more than original size.
              # But for the huge sigmas, one needs huge kernel and padding...
  
              ksize = min(ksize, cur_level.size(2), cur_level.size(3))
              if ksize % 2 == 0:
                  ksize += 1
  
              cur_level = gaussian_blur2d(
                  cur_level, (ksize, ksize), (sigma, sigma)
              )
              cur_sigma *= self.sigma_step
              pyr[-1].append(cur_level)
              sigmas[-1][:, level_idx] = cur_sigma
              pixel_dists[-1][:, level_idx] = pixel_distance
          _pyr = pyr[-1][-self.extra_levels]
          nextOctaveFirstLevel = _pyr[:, :, ::2, ::2]
  
          pixel_distance *= 2.0
          cur_sigma = self.init_sigma
          if (
              min(
                  nextOctaveFirstLevel.size(2),
                  nextOctaveFirstLevel.size(3),
              )
              <= self.min_size
          ):
              break
          pyr.append([nextOctaveFirstLevel])
          sigmas.append(
              cur_sigma
              * torch.ones(bs, self.n_levels + self.extra_levels).to(
                  x.device
              )
          )
          pixel_dists.append(
              pixel_distance
              * torch.ones(bs, self.n_levels + self.extra_levels).to(
                  x.device
              )
          )
          oct_idx += 1
  
      output_pyr = [stack(i, 2) for i in pyr]
  
      return output_pyr, sigmas, pixel_dists
  
imports: from __future__ import annotations
  import math
  import torch
  import torch.nn.functional
  from kornia.core import Module, Tensor, ones, pad, stack, tensor, zeros
  from kornia.core.check import KORNIA_CHECK, KORNIA_CHECK_IS_TENSOR, KORNIA_CHECK_SHAPE
  from kornia.filters import filter2d, gaussian_blur2d
label_desc: Use image node_id 5 for calling the ScalePyramid obj's underlying member method code with example usage: obj.forward(x) and returns: output_pyr,  sigmas,  pixel_dists