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}"}