scunet_model.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import os.path
  2. import sys
  3. import traceback
  4. import PIL.Image
  5. import numpy as np
  6. import torch
  7. from tqdm import tqdm
  8. from basicsr.utils.download_util import load_file_from_url
  9. import modules.upscaler
  10. from modules import devices, modelloader
  11. from scunet_model_arch import SCUNet as net
  12. from modules.shared import opts
  13. from modules import images
  14. class UpscalerScuNET(modules.upscaler.Upscaler):
  15. def __init__(self, dirname):
  16. self.name = "ScuNET"
  17. self.model_name = "ScuNET GAN"
  18. self.model_name2 = "ScuNET PSNR"
  19. self.model_url = "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_gan.pth"
  20. self.model_url2 = "https://github.com/cszn/KAIR/releases/download/v1.0/scunet_color_real_psnr.pth"
  21. self.user_path = dirname
  22. super().__init__()
  23. model_paths = self.find_models(ext_filter=[".pth"])
  24. scalers = []
  25. add_model2 = True
  26. for file in model_paths:
  27. if "http" in file:
  28. name = self.model_name
  29. else:
  30. name = modelloader.friendly_name(file)
  31. if name == self.model_name2 or file == self.model_url2:
  32. add_model2 = False
  33. try:
  34. scaler_data = modules.upscaler.UpscalerData(name, file, self, 4)
  35. scalers.append(scaler_data)
  36. except Exception:
  37. print(f"Error loading ScuNET model: {file}", file=sys.stderr)
  38. print(traceback.format_exc(), file=sys.stderr)
  39. if add_model2:
  40. scaler_data2 = modules.upscaler.UpscalerData(self.model_name2, self.model_url2, self)
  41. scalers.append(scaler_data2)
  42. self.scalers = scalers
  43. @staticmethod
  44. @torch.no_grad()
  45. def tiled_inference(img, model):
  46. # test the image tile by tile
  47. h, w = img.shape[2:]
  48. tile = opts.SCUNET_tile
  49. tile_overlap = opts.SCUNET_tile_overlap
  50. if tile == 0:
  51. return model(img)
  52. device = devices.get_device_for('scunet')
  53. assert tile % 8 == 0, "tile size should be a multiple of window_size"
  54. sf = 1
  55. stride = tile - tile_overlap
  56. h_idx_list = list(range(0, h - tile, stride)) + [h - tile]
  57. w_idx_list = list(range(0, w - tile, stride)) + [w - tile]
  58. E = torch.zeros(1, 3, h * sf, w * sf, dtype=img.dtype, device=device)
  59. W = torch.zeros_like(E, dtype=devices.dtype, device=device)
  60. with tqdm(total=len(h_idx_list) * len(w_idx_list), desc="ScuNET tiles") as pbar:
  61. for h_idx in h_idx_list:
  62. for w_idx in w_idx_list:
  63. in_patch = img[..., h_idx: h_idx + tile, w_idx: w_idx + tile]
  64. out_patch = model(in_patch)
  65. out_patch_mask = torch.ones_like(out_patch)
  66. E[
  67. ..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf
  68. ].add_(out_patch)
  69. W[
  70. ..., h_idx * sf: (h_idx + tile) * sf, w_idx * sf: (w_idx + tile) * sf
  71. ].add_(out_patch_mask)
  72. pbar.update(1)
  73. output = E.div_(W)
  74. return output
  75. def do_upscale(self, img: PIL.Image.Image, selected_file):
  76. torch.cuda.empty_cache()
  77. model = self.load_model(selected_file)
  78. if model is None:
  79. print(f"ScuNET: Unable to load model from {selected_file}", file=sys.stderr)
  80. return img
  81. device = devices.get_device_for('scunet')
  82. tile = opts.SCUNET_tile
  83. h, w = img.height, img.width
  84. np_img = np.array(img)
  85. np_img = np_img[:, :, ::-1] # RGB to BGR
  86. np_img = np_img.transpose((2, 0, 1)) / 255 # HWC to CHW
  87. torch_img = torch.from_numpy(np_img).float().unsqueeze(0).to(device) # type: ignore
  88. if tile > h or tile > w:
  89. _img = torch.zeros(1, 3, max(h, tile), max(w, tile), dtype=torch_img.dtype, device=torch_img.device)
  90. _img[:, :, :h, :w] = torch_img # pad image
  91. torch_img = _img
  92. torch_output = self.tiled_inference(torch_img, model).squeeze(0)
  93. torch_output = torch_output[:, :h * 1, :w * 1] # remove padding, if any
  94. np_output: np.ndarray = torch_output.float().cpu().clamp_(0, 1).numpy()
  95. del torch_img, torch_output
  96. torch.cuda.empty_cache()
  97. output = np_output.transpose((1, 2, 0)) # CHW to HWC
  98. output = output[:, :, ::-1] # BGR to RGB
  99. return PIL.Image.fromarray((output * 255).astype(np.uint8))
  100. def load_model(self, path: str):
  101. device = devices.get_device_for('scunet')
  102. if "http" in path:
  103. filename = load_file_from_url(url=self.model_url, model_dir=self.model_path, file_name="%s.pth" % self.name,
  104. progress=True)
  105. else:
  106. filename = path
  107. if not os.path.exists(os.path.join(self.model_path, filename)) or filename is None:
  108. print(f"ScuNET: Unable to load model from {filename}", file=sys.stderr)
  109. return None
  110. model = net(in_nc=3, config=[4, 4, 4, 4, 4, 4, 4], dim=64)
  111. model.load_state_dict(torch.load(filename), strict=True)
  112. model.eval()
  113. for k, v in model.named_parameters():
  114. v.requires_grad = False
  115. model = model.to(device)
  116. return model