code: def generate_perlin_noise_2d(shape, res):
      """Fractal perlin noise."""
  
      def f(t):
          return 6 * t**5 - 15 * t**4 + 10 * t**3
  
      delta = (res[0] / shape[0], res[1] / shape[1])
      d = (shape[0] // res[0], shape[1] // res[1])
      grid = (
          np.mgrid[
              0 : res[0] : delta[0], 0 : res[1] : delta[1]
          ].transpose(1, 2, 0)
          % 1
      )
      # Gradients
      angles = 2 * np.pi * np.random.rand(res[0] + 1, res[1] + 1)
      gradients = np.dstack((np.cos(angles), np.sin(angles)))
      g00 = gradients[0:-1, 0:-1].repeat(d[0], 0).repeat(d[1], 1)
      g10 = gradients[1:, 0:-1].repeat(d[0], 0).repeat(d[1], 1)
      g01 = gradients[0:-1, 1:].repeat(d[0], 0).repeat(d[1], 1)
      g11 = gradients[1:, 1:].repeat(d[0], 0).repeat(d[1], 1)
      # Ramps
      n00 = np.sum(grid * g00, 2)
      n10 = np.sum(
          np.dstack((grid[:, :, 0] - 1, grid[:, :, 1])) * g10, 2
      )
      n01 = np.sum(
          np.dstack((grid[:, :, 0], grid[:, :, 1] - 1)) * g01, 2
      )
      n11 = np.sum(
          np.dstack((grid[:, :, 0] - 1, grid[:, :, 1] - 1)) * g11, 2
      )
      # Interpolation
      t = f(grid)
      n0 = n00 * (1 - t[:, :, 0]) + t[:, :, 0] * n10
      n1 = n01 * (1 - t[:, :, 0]) + t[:, :, 0] * n11
      return np.sqrt(2) * ((1 - t[:, :, 1]) * n0 + t[:, :, 1] * n1)
  
file_path: anomalib/src/anomalib/data/utils/generators/perlin.py
imports: from __future__ import annotations
  import math
  import numpy
  import torch
  from torch import Tensor
label_desc: Use image node_id 3 for calling a global function with example usage: generate_perlin_noise_2d(shape, res) and returns: unknown, unknown