File size: 2,817 Bytes
4af5d6b
 
 
08212cf
4af5d6b
e411faa
4af5d6b
7673b7d
a40aa62
a27c2fe
4af5d6b
a27c2fe
 
 
 
 
1eb4d93
4af5d6b
 
a27c2fe
 
 
 
4af5d6b
a27c2fe
 
 
 
 
 
 
 
 
 
4af5d6b
a27c2fe
4af5d6b
 
a27c2fe
 
 
4af5d6b
a40aa62
a27c2fe
 
a40aa62
 
 
 
 
 
a27c2fe
 
 
 
7673b7d
a40aa62
a27c2fe
 
 
 
a40aa62
a27c2fe
3ca32a2
e411faa
4af5d6b
 
a27c2fe
4af5d6b
a27c2fe
 
 
 
4af5d6b
 
a27c2fe
 
 
 
 
 
4af5d6b
 
08212cf
a27c2fe
 
 
 
08212cf
4af5d6b
a27c2fe
 
 
fcbb94e
 
a27c2fe
 
 
4af5d6b
ebc3f8c
 
 
 
 
 
 
 
 
 
 
 
39326e5
ebc3f8c
 
a27c2fe
4af5d6b
 
a27c2fe
 
 
4af5d6b
a27c2fe
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
import gradio as gr
import torch
import torchvision.transforms as T
from PIL import Image
from diffusers import AsymmetricAutoencoderKL
import spaces

MODEL_ID = "AiArtLab/sdxs-1b"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32


# -------------------------
# Load VAE
# -------------------------
def load_vae(model_id=MODEL_ID):
    for attempt in (None, "vae"):
        try:
            if attempt is None:
                vae = AsymmetricAutoencoderKL.from_pretrained(
                    model_id,
                    torch_dtype=DTYPE
                )
            else:
                vae = AsymmetricAutoencoderKL.from_pretrained(
                    model_id,
                    subfolder=attempt,
                    torch_dtype=DTYPE
                )

            vae = vae.to(DEVICE)
            vae.eval()

            print("VAE loaded on", DEVICE)
            return vae

        except Exception as e:
            last_err = e

    raise RuntimeError(f"Failed to load VAE: {last_err}")


_vae = None


def get_vae():
    global _vae
    if _vae is None:
        _vae = load_vae()
    return _vae


# -------------------------
# Encode / Decode
# -------------------------
@spaces.GPU(duration=60)
def encode_decode(img: Image.Image):

    if img is None:
        raise gr.Error("Please upload an image")

    vae = get_vae()

    img = img.convert("RGB")

    tfm = T.Compose([
        T.ToTensor(),
        T.Normalize([0.5]*3, [0.5]*3),
    ])

    t = tfm(img).unsqueeze(0).to(DEVICE, dtype=DTYPE)

    print("Input tensor:", t.shape, t.dtype, t.device)

    with torch.no_grad():

        enc = vae.encode(t)
        lat = enc.latent_dist.sample()

        print("Latents:", lat.shape)

        dec = vae.decode(lat).sample

    x = (dec.clamp(-1, 1) + 1) * 127.5
    x = x.round().to(torch.uint8)

    x = x.squeeze(0).permute(1, 2, 0).cpu().numpy()

    out = Image.fromarray(x)

    print("Output size:", out.size)

    return out


# -------------------------
# UI
# -------------------------

with gr.Blocks(title="VAE True-to-Source Upscaler") as demo:
    gr.Markdown("# VAE-based 2x Upscaler\nSimple, blind, true-to-source upscaling without AI hallucinations.")
    
    with gr.Row():
        with gr.Column():
            input_img = gr.Image(type="pil", label="Upload Image")
            upscale_btn = gr.Button("Upscale x2", variant="primary")
        with gr.Column():
            output_img = gr.Image(type="pil", label="Result (x2)")

    # Привязка кнопки к функции
    upscale_btn.click(
        fn=encode_decode, 
        inputs=input_img, 
        outputs=output_img
    )


# -------------------------
# Launch
# -------------------------
if __name__ == "__main__":
    demo.launch()