File size: 5,800 Bytes
2389e63 7c16a09 e004dc4 7c16a09 2389e63 7c16a09 db978c8 7c16a09 2389e63 7c16a09 db978c8 7c16a09 e004dc4 db978c8 7c16a09 e004dc4 7c16a09 db978c8 e004dc4 7c16a09 2389e63 7c16a09 db978c8 7c16a09 e004dc4 db978c8 e004dc4 7c16a09 db978c8 e004dc4 db978c8 e004dc4 7c16a09 e004dc4 7c16a09 2389e63 7c16a09 2389e63 7c16a09 db978c8 e004dc4 7c16a09 db978c8 7c16a09 2389e63 7c16a09 2389e63 7c16a09 2389e63 7c16a09 db978c8 7c16a09 e004dc4 7c16a09 2389e63 7c16a09 db978c8 7c16a09 db978c8 e004dc4 db978c8 e004dc4 7c16a09 db978c8 7c16a09 e004dc4 db978c8 7c16a09 db978c8 7c16a09 e004dc4 db978c8 7c16a09 2389e63 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | import io
import os
import cv2
import torch
import base64
import logging
import requests
import numpy as np
from PIL import Image
from gfpgan import GFPGANer
# ======================================================
# LOGGING CONFIGURATION
# ======================================================
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logger.debug("π¦ [INIT] Importing GFPGAN handler module...")
# ======================================================
# GFPGAN MODEL URL
# ======================================================
MODEL_URL = "https://github.com/TencentARC/GFPGAN/releases/download/v1.3.0/GFPGANv1.4.pth"
MODEL_NAME = "GFPGANv1.4.pth"
# ======================================================
# ENDPOINT HANDLER
# ======================================================
class EndpointHandler:
def __init__(self, path="."):
logger.debug("π [INIT] Starting GFPGAN EndpointHandler initialization...")
logger.debug(f"π Working directory: {os.getcwd()}")
logger.debug(f"π Handler path argument: {path}")
model_path = os.path.join(path, MODEL_NAME)
logger.debug(f"π [MODEL] Expected model path: {model_path}")
# ------------------------------------------------------
# Download model if missing
# ------------------------------------------------------
if not os.path.exists(model_path):
try:
logger.debug(f"π₯ [DOWNLOAD] Model not found locally β fetching from {MODEL_URL}")
r = requests.get(MODEL_URL, stream=True)
r.raise_for_status()
with open(model_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
logger.debug("β
[MODEL] Downloaded GFPGAN weights successfully.")
except Exception as e:
logger.error(f"π₯ [ERROR] Failed to download GFPGAN weights: {e}")
raise
# ------------------------------------------------------
# Initialize GFPGANer (same as official Gradio demo)
# ------------------------------------------------------
try:
logger.debug("π§ [MODEL] Initializing GFPGANer (upscale=2, arch='clean')...")
self.restorer = GFPGANer(
model_path=model_path,
upscale=2, # Rescaling factor = 2
arch="clean",
channel_multiplier=2,
bg_upsampler=None
)
logger.debug("β
[MODEL] GFPGAN model initialized successfully.")
except Exception as e:
logger.error(f"π₯ [ERROR] Model initialization failed: {e}")
raise
# ======================================================
# INFERENCE CALL
# ======================================================
def __call__(self, data):
logger.debug("βοΈ [INFER] Starting inference...")
logger.debug(f"π₯ Incoming data type: {type(data)}")
# ------------------------------------------------------
# Handle both JSON base64 and raw bytes
# ------------------------------------------------------
try:
if isinstance(data, dict) and "inputs" in data:
logger.debug("π¦ Detected JSON base64 input")
image_bytes = base64.b64decode(data["inputs"])
elif isinstance(data, (bytes, bytearray)):
logger.debug("π¦ Detected raw bytes input")
image_bytes = data
else:
raise ValueError("Unsupported input format β expected bytes or base64 data")
logger.debug(f"π§Ύ [BYTES] Received {len(image_bytes)} bytes")
except Exception as e:
logger.error(f"π₯ [ERROR] Input parsing failed: {e}")
return {"error": f"Invalid input: {e}"}
# ------------------------------------------------------
# Decode image
# ------------------------------------------------------
try:
img_np = np.array(Image.open(io.BytesIO(image_bytes)).convert("RGB"))
logger.debug(f"πΌοΈ [IMAGE] Loaded image of shape: {img_np.shape}")
except Exception as e:
logger.error(f"π₯ [ERROR] Failed to load image: {e}")
return {"error": f"Image loading failed: {e}"}
# ------------------------------------------------------
# Run GFPGAN restoration
# ------------------------------------------------------
try:
cropped_faces, restored_faces, restored_img = self.restorer.enhance(
img_np,
has_aligned=False,
only_center_face=False,
paste_back=True # Matches GFPGAN web demo
)
logger.debug("β
[RESTORE] Face restoration completed successfully.")
except Exception as e:
logger.error(f"π₯ [ERROR] GFPGAN enhancement failed: {e}")
return {"error": f"Enhancement failed: {e}"}
# ------------------------------------------------------
# Encode result as base64 PNG
# ------------------------------------------------------
try:
_, buffer = cv2.imencode(".png", restored_img[:, :, ::-1]) # BGRβRGB
img_base64 = base64.b64encode(buffer).decode("utf-8")
logger.debug("π€ [ENCODE] Encoded restored image successfully.")
return {"image": img_base64}
except Exception as e:
logger.error(f"π₯ [ERROR] Failed to encode image: {e}")
return {"error": f"Encoding failed: {e}"}
|