Spaces:
Running
Running
| # services/deblur.py | |
| import os | |
| import torch | |
| import torch.nn as nn | |
| import numpy as np | |
| from PIL import Image | |
| from models.fpn_inception import FPNInception | |
| # ===================== | |
| # 初始化模型 | |
| # ===================== | |
| device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
| print(f"🔹 [DeblurService] Using device: {device}") | |
| checkpoint_path = os.path.join("model", "deblurgan_v2_latest.pth") | |
| G = FPNInception(norm_layer=nn.InstanceNorm2d).to(device) | |
| checkpoint = torch.load(checkpoint_path, map_location=device) | |
| G.load_state_dict(checkpoint["G"], strict=False) | |
| G.eval() | |
| print(f"✅ [DeblurService] Model loaded from {checkpoint_path}") | |
| # ✅ 去模糊函式 | |
| def deblur_image_tiled(img, tile_size=512, overlap=32): | |
| w, h = img.size | |
| new_w = (w // 32) * 32 | |
| new_h = (h // 32) * 32 | |
| if new_w != w or new_h != h: | |
| img = img.resize((new_w, new_h), Image.BICUBIC) | |
| w, h = new_w, new_h | |
| img_np = np.array(img).astype(np.float32) / 255.0 | |
| img_tensor = torch.from_numpy(img_np).permute(2, 0, 1).unsqueeze(0).to(device) | |
| stride = tile_size - overlap | |
| tiles_x = list(range(0, w, stride)) | |
| tiles_y = list(range(0, h, stride)) | |
| if tiles_x[-1] + tile_size > w: | |
| tiles_x[-1] = w - tile_size | |
| if tiles_y[-1] + tile_size > h: | |
| tiles_y[-1] = h - tile_size | |
| output = torch.zeros_like(img_tensor) | |
| weight = torch.zeros_like(img_tensor) | |
| with torch.no_grad(): | |
| for y in tiles_y: | |
| for x in tiles_x: | |
| patch = img_tensor[:, :, y:y+tile_size, x:x+tile_size] | |
| pred = G(patch) # ✅ 改用 services 裡的模型 G | |
| output[:, :, y:y+tile_size, x:x+tile_size] += pred | |
| weight[:, :, y:y+tile_size, x:x+tile_size] += 1.0 | |
| output /= weight | |
| output = torch.clamp(output, 0, 1) | |
| out_np = (output.squeeze().permute(1, 2, 0).cpu().numpy() * 255.0).astype(np.uint8) | |
| return Image.fromarray(out_np) | |