Tanut commited on
Commit
fc29d6e
·
1 Parent(s): a2495ec

Reset Project

Browse files
Files changed (3) hide show
  1. README.md +5 -12
  2. app.py +75 -168
  3. requirements.txt +1 -3
README.md CHANGED
@@ -1,17 +1,10 @@
1
  ---
2
- title: GenImages ControlNet
3
- emoji: 🐢
4
- colorFrom: green
5
- colorTo: green
6
  sdk: gradio
7
- sdk_version: 5.42.0
8
- app_file: app.py
9
- pinned: false
10
- license: openrail
11
- short_description: Testing generate Images from Controlnet
12
  preload_from_hub:
13
  - runwayml/stable-diffusion-v1-5
14
- - monster-labs/control_v1p_sd15_qrcode_monster
15
  ---
16
-
17
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: ZeroGPU SD1.5 Minimal
3
+ emoji: 🎨
 
 
4
  sdk: gradio
5
+ sdk_version: 4.44.1
6
+ python_version: 3.10.13
 
 
 
7
  preload_from_hub:
8
  - runwayml/stable-diffusion-v1-5
9
+ license: openrail++
10
  ---
 
 
app.py CHANGED
@@ -1,204 +1,111 @@
1
  import os, gc, random
2
  import gradio as gr
3
- import numpy as np
4
  from PIL import Image
5
- import qrcode
6
- from qrcode.constants import ERROR_CORRECT_H
7
 
8
- import torch
9
- import spaces # <- ZeroGPU decorator
10
-
11
- from diffusers import (
12
- StableDiffusionPipeline,
13
- StableDiffusionControlNetPipeline,
14
- ControlNetModel,
15
- )
16
- from diffusers.schedulers.scheduling_euler_discrete import EulerDiscreteScheduler
17
- from controlnet_aux import CannyDetector
18
-
19
- # -----------------------------
20
- # Versions / env
21
- # -----------------------------
22
- TORCH_DTYPE = torch.float16 # Spaces GPU slice supports fp16 well
23
-
24
- # Optional (private models): set HF_TOKEN in Space secrets
25
- HF_TOKEN = os.getenv("HF_TOKEN")
26
  AUTH = {"token": HF_TOKEN} if HF_TOKEN else {}
27
 
28
- # -----------------------------
29
- # Global caches (lazy)
30
- # -----------------------------
31
- _sd_txt = {"pipe": None}
32
- _sd_cn = {"pipe": None, "canny": None}
33
-
34
- BASE_15 = "runwayml/stable-diffusion-v1-5"
35
- CN_CANNY_15 = "lllyasviel/sd-controlnet-canny"
36
- CN_TILE_15 = "lllyasviel/control_v11f1e_sd15_tile"
37
-
38
- NEG_DEFAULT = "lowres, low contrast, blurry, jpeg artifacts, worst quality, bad anatomy, extra digits"
39
-
40
 
41
- # -----------------------------
42
- # QR maker (unchanged behavior)
43
- # -----------------------------
44
- def make_qr(url: str = "http://www.mybirdfire.com", size: int = 512, border: int = 4) -> Image.Image:
45
- qr = qrcode.QRCode(version=None, error_correction=ERROR_CORRECT_H, box_size=10, border=int(border))
46
- qr.add_data(url.strip())
47
- qr.make(fit=True)
48
- img = qr.make_image(fill_color="black", back_color="white").convert("RGB")
49
- return img.resize((int(size), int(size)), resample=Image.NEAREST)
50
-
51
-
52
- # -----------------------------
53
- # Lazy loaders (Spaces-safe)
54
- # -----------------------------
55
- def _get_sd15_txt2img():
56
- if _sd_txt["pipe"] is None:
57
  pipe = StableDiffusionPipeline.from_pretrained(
58
- BASE_15,
59
- torch_dtype=TORCH_DTYPE,
60
  safety_checker=None,
61
  use_safetensors=True,
62
  low_cpu_mem_usage=True,
63
  **AUTH
64
  )
65
- # Memory savers — ok to call before GPU is attached
66
- pipe.enable_attention_slicing()
67
- pipe.enable_vae_slicing()
68
- pipe.enable_model_cpu_offload()
69
- _sd_txt["pipe"] = pipe
70
- return _sd_txt["pipe"]
71
-
72
- def _get_sd15_canny_tile():
73
- if _sd_cn["pipe"] is None:
74
- canny = ControlNetModel.from_pretrained(CN_CANNY_15, torch_dtype=TORCH_DTYPE, use_safetensors=True, **AUTH)
75
- tile = ControlNetModel.from_pretrained(CN_TILE_15, torch_dtype=TORCH_DTYPE, use_safetensors=True, **AUTH)
76
-
77
- pipe = StableDiffusionControlNetPipeline.from_pretrained(
78
- BASE_15,
79
- controlnet=[canny, tile],
80
- torch_dtype=TORCH_DTYPE,
81
- safety_checker=None,
82
- use_safetensors=True,
83
- low_cpu_mem_usage=True,
84
- **AUTH
85
  )
86
- pipe.scheduler = EulerDiscreteScheduler.from_config(pipe.scheduler.config)
87
  pipe.enable_attention_slicing()
88
  pipe.enable_vae_slicing()
89
  pipe.enable_model_cpu_offload()
90
-
91
- _sd_cn["pipe"] = pipe
92
- _sd_cn["canny"] = CannyDetector()
93
- return _sd_cn["pipe"], _sd_cn["canny"]
94
-
95
-
96
- # -----------------------------
97
- # SD 1.5 (prompt-only)
98
- # -----------------------------
99
- @spaces.GPU(duration=120)
100
- def sd_generate(prompt, negative, steps, guidance, seed):
101
- pipe = _get_sd15_txt2img()
102
-
103
- # Reproducible generator on CUDA (available during @GPU call)
 
 
 
 
 
 
 
 
104
  g = torch.Generator(device="cuda")
105
- g = g.manual_seed(int(seed)) if int(seed) != 0 else g.manual_seed(random.randint(0, 2**31 - 1))
 
 
106
 
107
  if torch.cuda.is_available():
108
  torch.cuda.empty_cache()
109
  gc.collect()
110
 
111
- with torch.autocast(device_type="cuda", dtype=TORCH_DTYPE):
112
  out = pipe(
113
  prompt=str(prompt),
114
- negative_prompt=(negative or ""),
115
  num_inference_steps=int(steps),
116
- guidance_scale=float(guidance),
117
- generator=g
 
118
  )
119
- return out.images[0]
120
-
121
 
122
- # -----------------------------
123
- # Stylizer (SD1.5 + ControlNet canny + tile)
124
- # -----------------------------
125
- @spaces.GPU(duration=180)
126
- def stylize_qr_sd15(prompt: str, negative: str, steps: int, guidance: float, seed: int,
127
- canny_low: int, canny_high: int, border: int):
128
-
129
- pipe, canny = _get_sd15_canny_tile()
130
-
131
- # Fresh QR → edges
132
- qr_img = make_qr("http://www.mybirdfire.com", size=512, border=int(border))
133
- edges = canny(qr_img, low_threshold=int(canny_low), high_threshold=int(canny_high))
134
-
135
- # Control weights (canny, tile). Tune to taste.
136
- cn_scales = [1.2, 0.6]
137
-
138
- g = torch.Generator(device="cuda")
139
- g = g.manual_seed(int(seed)) if int(seed) != 0 else g.manual_seed(random.randint(0, 2**31 - 1))
140
-
141
- if torch.cuda.is_available():
142
- torch.cuda.empty_cache()
143
- gc.collect()
144
-
145
- with torch.autocast(device_type="cuda", dtype=TORCH_DTYPE):
146
- out = pipe(
147
- prompt=str(prompt),
148
- negative_prompt=(negative or NEG_DEFAULT),
149
- image=[edges, qr_img], # txt2img ControlNet: control images
150
- controlnet_conditioning_scale=cn_scales,
151
- num_inference_steps=int(steps),
152
- guidance_scale=float(guidance),
153
- generator=g
154
- )
155
- return out.images[0]
156
-
157
-
158
- # -----------------------------
159
- # UI (same layout as yours)
160
- # -----------------------------
161
  with gr.Blocks() as demo:
162
- gr.Markdown("## Stable Diffusion + QR Code + ControlNet (SD1.5) ZeroGPU")
163
-
164
- with gr.Tab("Stable Diffusion (prompt → image)"):
165
- prompt = gr.Textbox(label="Prompt", value="Sky, Moon, Bird, Blue, In the dark, Goddess, Sweet, Beautiful, Fantasy, Art, Anime")
166
- negative = gr.Textbox(label="Negative Prompt", value="lowres, bad anatomy, worst quality")
167
- steps = gr.Slider(10, 50, value=30, label="Steps", step=1)
168
- cfg = gr.Slider(1, 12, value=7.0, label="Guidance Scale", step=0.1)
169
- seed = gr.Number(value=0, label="Seed (0 = random)", precision=0)
170
- out_sd = gr.Image(label="Generated Image")
171
- gr.Button("Generate").click(sd_generate, [prompt, negative, steps, cfg, seed], out_sd)
172
-
173
- with gr.Tab("QR Maker (mybirdfire)"):
174
- url = gr.Textbox(label="URL/Text", value="http://www.mybirdfire.com")
175
- size = gr.Slider(256, 1024, value=512, step=64, label="Size (px)")
176
- quiet = gr.Slider(0, 8, value=4, step=1, label="Border (quiet zone)")
177
- out_qr = gr.Image(label="QR Code", type="pil")
178
- gr.Button("Generate QR").click(make_qr, [url, size, quiet], out_qr)
179
-
180
- with gr.Tab("QR Stylizer (SD1.5 canny + tile, Euler)"):
181
- s_prompt = gr.Textbox(label="Style Prompt", value="Sky, Moon, Bird, Blue, In the dark, Goddess, Sweet, Beautiful, Fantasy, Art, Anime")
182
- s_negative = gr.Textbox(label="Negative Prompt", value=NEG_DEFAULT)
183
- s_steps = gr.Slider(10, 50, value=28, label="Steps", step=1)
184
- s_cfg = gr.Slider(1, 12, value=7.0, label="CFG", step=0.1)
185
- s_seed = gr.Number(value=1470713301, label="Seed", precision=0)
186
- canny_l = gr.Slider(0, 255, value=80, step=1, label="Canny low")
187
- canny_h = gr.Slider(0, 255, value=160, step=1, label="Canny high")
188
- s_border = gr.Slider(2, 10, value=6, step=1, label="QR border")
189
- out_styl = gr.Image(label="Stylized QR")
190
- gr.Button("Stylize").click(
191
- stylize_qr_sd15,
192
- [s_prompt, s_negative, s_steps, s_cfg, s_seed, canny_l, canny_h, s_border],
193
- out_styl
194
- )
195
 
196
  if __name__ == "__main__":
197
- demo.queue(max_size=12).launch(
 
198
  server_name="0.0.0.0",
199
  server_port=7860,
200
  share=True,
201
  show_api=True,
202
- analytics_enabled=False
203
  )
204
-
 
1
  import os, gc, random
2
  import gradio as gr
3
+ import torch, spaces
4
  from PIL import Image
5
+ from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
 
6
 
7
+ # ---- config ----
8
+ MODEL_ID = "runwayml/stable-diffusion-v1-5"
9
+ DTYPE = torch.float16
10
+ HF_TOKEN = os.getenv("HF_TOKEN") # optional (only needed for private models)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  AUTH = {"token": HF_TOKEN} if HF_TOKEN else {}
12
 
13
+ # cache for lazy loading
14
+ _PIPE = {"sd": None}
 
 
 
 
 
 
 
 
 
 
15
 
16
+ def _get_pipe():
17
+ """Lazy-load SD1.5 and enable memory savers for ZeroGPU."""
18
+ if _PIPE["sd"] is None:
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  pipe = StableDiffusionPipeline.from_pretrained(
20
+ MODEL_ID,
21
+ torch_dtype=DTYPE,
22
  safety_checker=None,
23
  use_safetensors=True,
24
  low_cpu_mem_usage=True,
25
  **AUTH
26
  )
27
+ pipe.scheduler = DPMSolverMultistepScheduler.from_config(
28
+ pipe.scheduler.config, use_karras_sigmas=True, algorithm_type="dpmsolver++"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  )
 
30
  pipe.enable_attention_slicing()
31
  pipe.enable_vae_slicing()
32
  pipe.enable_model_cpu_offload()
33
+ _PIPE["sd"] = pipe
34
+ return _PIPE["sd"]
35
+
36
+ def _snap_dim(x: int) -> int:
37
+ # diffusers likes multiples of 8; stay within safe VRAM for ZeroGPU
38
+ x = max(256, min(1024, int(x)))
39
+ return x - (x % 8)
40
+
41
+ @spaces.GPU(duration=120) # allocate a ZeroGPU slice only during generation
42
+ def generate(prompt: str,
43
+ negative_prompt: str,
44
+ steps: int,
45
+ guidance_scale: float,
46
+ width: int,
47
+ height: int,
48
+ seed: int):
49
+ pipe = _get_pipe()
50
+
51
+ w = _snap_dim(width)
52
+ h = _snap_dim(height)
53
+
54
+ # seed handling (reproducible on CUDA)
55
  g = torch.Generator(device="cuda")
56
+ if int(seed) == -1:
57
+ seed = random.randint(0, 2**31 - 1)
58
+ g = g.manual_seed(int(seed))
59
 
60
  if torch.cuda.is_available():
61
  torch.cuda.empty_cache()
62
  gc.collect()
63
 
64
+ with torch.autocast(device_type="cuda", dtype=DTYPE):
65
  out = pipe(
66
  prompt=str(prompt),
67
+ negative_prompt=str(negative_prompt or ""),
68
  num_inference_steps=int(steps),
69
+ guidance_scale=float(guidance_scale),
70
+ width=w, height=h,
71
+ generator=g,
72
  )
73
+ img: Image.Image = out.images[0]
74
+ return img, seed
75
 
76
+ # ---------- UI ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  with gr.Blocks() as demo:
78
+ gr.Markdown("# 🧩 Stable Diffusion 1.5 (ZeroGPU)\nText prompt image, lean & fast.")
79
+
80
+ with gr.Row():
81
+ with gr.Column():
82
+ prompt = gr.Textbox(
83
+ label="Prompt",
84
+ value="a cozy reading nook with warm sunlight, soft textures, cinematic lighting, highly detailed"
85
+ )
86
+ negative = gr.Textbox(
87
+ label="Negative prompt",
88
+ value="lowres, blurry, watermark, text, logo, nsfw"
89
+ )
90
+ steps = gr.Slider(4, 50, value=28, step=1, label="Steps")
91
+ cfg = gr.Slider(1.0, 12.0, value=7.0, step=0.5, label="CFG scale")
92
+ width = gr.Slider(256, 1024, value=640, step=16, label="Width")
93
+ height = gr.Slider(256, 1024, value=640, step=16, label="Height")
94
+ seed = gr.Number(value=-1, precision=0, label="Seed (-1 = random)")
95
+
96
+ btn = gr.Button("Generate", variant="primary")
97
+ with gr.Column():
98
+ out_img = gr.Image(label="Result", interactive=False)
99
+ out_seed = gr.Number(label="Used seed", interactive=False)
100
+
101
+ btn.click(generate, [prompt, negative, steps, cfg, width, height, seed], [out_img, out_seed])
 
 
 
 
 
 
 
 
 
102
 
103
  if __name__ == "__main__":
104
+ # Spaces requires a shareable link (no localhost)
105
+ demo.queue(max_size=8).launch(
106
  server_name="0.0.0.0",
107
  server_port=7860,
108
  share=True,
109
  show_api=True,
110
+ analytics_enabled=False,
111
  )
 
requirements.txt CHANGED
@@ -4,10 +4,8 @@ transformers>=4.42.0
4
  accelerate>=0.31.0
5
  safetensors
6
  gradio==4.44.1
7
- qrcode[pil]
8
- Pillow
9
  huggingface-hub
10
- controlnet-aux>=0.0.8
11
  spaces
12
  numpy
13
  mediapipe
 
4
  accelerate>=0.31.0
5
  safetensors
6
  gradio==4.44.1
 
 
7
  huggingface-hub
8
+ Pillow
9
  spaces
10
  numpy
11
  mediapipe