code: def degradation_bsrgan(img, sf=4, lq_patchsize=72, isp_model=None):
      """
      This is the degradation model of BSRGAN from the paper
      "Designing a Practical Degradation Model for Deep Blind Image Super-Resolution"
      ----------
      img: HXWXC, [0, 1], its size should be large than (lq_patchsizexsf)x(lq_patchsizexsf)
      sf: scale factor
      isp_model: camera ISP model
      Returns
      -------
      img: low-quality patch, size: lq_patchsizeXlq_patchsizeXC, range: [0, 1]
      hq: corresponding high-quality patch, size: (lq_patchsizexsf)X(lq_patchsizexsf)XC, range: [0, 1]
      """
      isp_prob, jpeg_prob, scale2_prob = 0.25, 0.9, 0.25
      sf_ori = sf
  
      h1, w1 = img.shape[:2]
      img = img.copy()[: w1 - w1 % sf, : h1 - h1 % sf, ...]  # mod crop
      h, w = img.shape[:2]
  
      if h < lq_patchsize * sf or w < lq_patchsize * sf:
          raise ValueError(f"img size ({h1}X{w1}) is too small!")
  
      hq = img.copy()
  
      if sf == 4 and random.random() < scale2_prob:  # downsample1
          if np.random.rand() < 0.5:
              img = cv2.resize(
                  img,
                  (
                      int(1 / 2 * img.shape[1]),
                      int(1 / 2 * img.shape[0]),
                  ),
                  interpolation=random.choice([1, 2, 3]),
              )
          else:
              img = util.imresize_np(img, 1 / 2, True)
          img = np.clip(img, 0.0, 1.0)
          sf = 2
  
      shuffle_order = random.sample(range(7), 7)
      idx1, idx2 = shuffle_order.index(2), shuffle_order.index(3)
      if idx1 > idx2:  # keep downsample3 last
          shuffle_order[idx1], shuffle_order[idx2] = (
              shuffle_order[idx2],
              shuffle_order[idx1],
          )
  
      for i in shuffle_order:
          if i == 0:
              img = add_blur(img, sf=sf)
  
          elif i == 1:
              img = add_blur(img, sf=sf)
  
          elif i == 2:
              a, b = img.shape[1], img.shape[0]
              # downsample2
              if random.random() < 0.75:
                  sf1 = random.uniform(1, 2 * sf)
                  img = cv2.resize(
                      img,
                      (
                          int(1 / sf1 * img.shape[1]),
                          int(1 / sf1 * img.shape[0]),
                      ),
                      interpolation=random.choice([1, 2, 3]),
                  )
              else:
                  k = fspecial(
                      "gaussian", 25, random.uniform(0.1, 0.6 * sf)
                  )
                  k_shifted = shift_pixel(k, sf)
                  k_shifted = (
                      k_shifted / k_shifted.sum()
                  )  # blur with shifted kernel
                  img = ndimage.convolve(
                      img,
                      np.expand_dims(k_shifted, axis=2),
                      mode="mirror",
                  )
                  img = img[0::sf, 0::sf, ...]  # nearest downsampling
              img = np.clip(img, 0.0, 1.0)
  
          elif i == 3:
              # downsample3
              img = cv2.resize(
                  img,
                  (int(1 / sf * a), int(1 / sf * b)),
                  interpolation=random.choice([1, 2, 3]),
              )
              img = np.clip(img, 0.0, 1.0)
  
          elif i == 4:
              # add Gaussian noise
              img = add_Gaussian_noise(
                  img, noise_level1=2, noise_level2=8
              )
  
          elif i == 5:
              # add JPEG noise
              if random.random() < jpeg_prob:
                  img = add_JPEG_noise(img)
  
          elif i == 6:
              # add processed camera sensor noise
              if random.random() < isp_prob and isp_model is not None:
                  with torch.no_grad():
                      img, hq = isp_model.forward(img.copy(), hq)
  
      # add final JPEG compression noise
      img = add_JPEG_noise(img)
  
      # random crop
      img, hq = random_crop(img, hq, sf_ori, lq_patchsize)
  
      return img, hq
  
imports: import random
  from functools import partial
  import albumentations
  import cv2
  import ldm.modules.image_degradation.utils_image
  import numpy
  import scipy
  import scipy.stats
  import torch
  from scipy import ndimage
  from scipy.interpolate import interp2d
  from scipy.linalg import orth
label_desc: Use image node_id 23 for calling a global function with example usage: degradation_bsrgan(img, sf, lq_patchsize, isp_model) and returns: img,  hq