Leteint commited on
Commit
4694545
·
verified ·
1 Parent(s): 1d5c062

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +208 -56
app.py CHANGED
@@ -3,105 +3,257 @@ import json
3
  import os
4
  import torch
5
  import gradio as gr
 
6
  from diffusers import AutoPipelineForText2Image
7
  from PIL import PngImagePlugin
8
 
 
 
 
9
  BASE_MODEL_ID = "stabilityai/stable-diffusion-xl-base-1.0"
10
- LORA_FACE_REPO = "akash-guptag/Detailers_By_Stable_Yogi"
 
 
 
 
 
 
11
  LORA_FACE_ADAPTER = "face_detail"
12
 
13
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
14
  DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
15
 
 
 
 
 
16
  pipe = AutoPipelineForText2Image.from_pretrained(
17
  BASE_MODEL_ID,
18
  torch_dtype=DTYPE,
19
  variant="fp16" if DTYPE == torch.float16 else None,
20
- safety_checker=None,
21
  requires_safety_checker=False,
22
  )
23
  pipe.to(DEVICE)
24
 
25
- def dummy_checker(images, **kwargs):
 
 
26
  return images, [False] * len(images)
27
- pipe.safety_checker = dummy_checker
28
 
 
29
  pipe.set_progress_bar_config(disable=True)
 
 
 
 
 
30
  pipe.load_lora_weights(LORA_FACE_REPO, adapter_name=LORA_FACE_ADAPTER)
31
 
32
  os.makedirs("outputs", exist_ok=True)
33
 
 
 
 
 
34
  @spaces.GPU()
35
- def generate(prompt, negative, seed_str, steps, guidance, width, height, face_weight, filename):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
- if isinstance(seed_str, str) and seed_str.strip():
38
- seed = int(float(seed_str))
 
39
  else:
40
- seed = int(seed_str) if seed_str is not None else -1
41
-
42
 
43
- generator = torch.Generator(DEVICE).manual_seed(seed) if seed >= 0 else torch.Generator(DEVICE)
 
 
 
44
 
45
- pipe.set_adapters([LORA_FACE_ADAPTER], adapter_weights=[float(face_weight)])
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- result = pipe(
48
- prompt=prompt or "masterpiece",
49
- negative_prompt=negative,
50
- num_inference_steps=int(steps),
51
- guidance_scale=float(guidance),
52
- width=int(width),
53
- height=int(height),
54
- generator=generator,
55
- ).images[0]
56
 
57
- safe_name = filename.strip().replace(" ", "_") or "sdxl_nsfw"
58
- img_path = f"outputs/{safe_name}.png"
59
- json_path = f"outputs/{safe_name}.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
 
61
- metadata = {"prompt": prompt, "seed": seed, "steps": int(steps)}
62
-
63
  pnginfo = PngImagePlugin.PngInfo()
64
- pnginfo.add_text("params", json.dumps(metadata))
65
- result.save(img_path, pnginfo=pnginfo)
 
 
 
66
 
67
- with open(json_path, "w") as f:
68
- json.dump(metadata, f, indent=2)
69
 
70
- return result, json.dumps(metadata, indent=2), json_path
71
 
72
- with gr.Blocks(title="🔥 SDXL NSFW + Detail LoRA") as demo:
73
- gr.Markdown("# SDXL 1.0 **NSFW** ✅ Face Detail LoRA")
74
-
 
 
 
 
 
 
 
 
75
  with gr.Row():
76
  with gr.Column():
77
  prompt = gr.Textbox(
78
- label="Prompt",
79
- placeholder="1girl nude, masterpiece, detailed anatomy, realistic skin, sharp face",
 
80
  lines=4,
81
- value=None # ← FIX: vide, valeur dans placeholder
82
  )
83
  negative = gr.Textbox(
84
- label="Negative Prompt",
85
- placeholder="blurry, deformed, ugly, low quality, extra limbs",
86
- value=None
 
 
 
 
 
 
87
  )
88
- seed = gr.Number(label="Seed", value=-1)
89
- steps = gr.Slider(20, 50, 35, step=1, label="Steps")
90
- guidance = gr.Slider(5.0, 12.0, 7.5, step=0.1, label="Guidance")
91
- width = gr.Slider(512, 1536, 1024, step=64, label="Width")
92
- height = gr.Slider(512, 1536, 1024, step=64, label="Height")
93
- lora_weight = gr.Slider(0.0, 1.2, 0.7, step=0.05, label="LoRA Weight")
94
- filename = gr.Textbox(label="Filename", value="nsfw_test")
95
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  with gr.Column():
97
- output_image = gr.Image(label="Generated Image")
98
- output_metadata = gr.Textbox(label="Metadata JSON", lines=10)
99
- output_file = gr.File(label="📥 Download JSON")
100
-
101
- gr.Button("🚀 Generate NSFW", variant="primary", size="lg").click(
102
- generate,
103
- [prompt, negative, seed, steps, guidance, width, height, lora_weight, filename],
104
- [output_image, output_metadata, output_file]
105
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
- demo.launch()
 
 
3
  import os
4
  import torch
5
  import gradio as gr
6
+
7
  from diffusers import AutoPipelineForText2Image
8
  from PIL import PngImagePlugin
9
 
10
+ # -----------------------
11
+ # Config modèles
12
+ # -----------------------
13
  BASE_MODEL_ID = "stabilityai/stable-diffusion-xl-base-1.0"
14
+
15
+ # LoRA "wrong" (améliore qualité + adhérence prompt)
16
+ LORA_WRONG_REPO = "minimaxir/sdxl-wrong-lora" # [web:183]
17
+ LORA_WRONG_ADAPTER = "wrong_prompt"
18
+
19
+ # LoRA visages / détails
20
+ LORA_FACE_REPO = "akash-guptag/Detailers_By_Stable_Yogi" # [web:60][web:63]
21
  LORA_FACE_ADAPTER = "face_detail"
22
 
23
  DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
24
  DTYPE = torch.float16 if DEVICE == "cuda" else torch.float32
25
 
26
+ # -----------------------
27
+ # Chargement pipeline
28
+ # -----------------------
29
+ print("Chargement SDXL...")
30
  pipe = AutoPipelineForText2Image.from_pretrained(
31
  BASE_MODEL_ID,
32
  torch_dtype=DTYPE,
33
  variant="fp16" if DTYPE == torch.float16 else None,
34
+ safety_checker=None, # on désactive officiellement
35
  requires_safety_checker=False,
36
  )
37
  pipe.to(DEVICE)
38
 
39
+ # Monkey-patch du safety checker (NSFW fully bypass)
40
+ def dummy_safety_checker(images, **kwargs):
41
+ # renvoie toujours "tout va bien"
42
  return images, [False] * len(images)
 
43
 
44
+ pipe.safety_checker = dummy_safety_checker
45
  pipe.set_progress_bar_config(disable=True)
46
+
47
+ print("Chargement LoRA WRONG (prompt/qualité)...")
48
+ pipe.load_lora_weights(LORA_WRONG_REPO, adapter_name=LORA_WRONG_ADAPTER)
49
+
50
+ print("Chargement LoRA face/detail (Stable Yogi)...")
51
  pipe.load_lora_weights(LORA_FACE_REPO, adapter_name=LORA_FACE_ADAPTER)
52
 
53
  os.makedirs("outputs", exist_ok=True)
54
 
55
+
56
+ # -----------------------
57
+ # Fonction de génération
58
+ # -----------------------
59
  @spaces.GPU()
60
+ def generate(
61
+ prompt: str,
62
+ negative: str,
63
+ seed_in,
64
+ steps: float,
65
+ guidance: float,
66
+ width: float,
67
+ height: float,
68
+ face_weight: float,
69
+ script_name: str,
70
+ ):
71
+ # Seed robuste (Gradio envoie parfois int, parfois str)
72
+ try:
73
+ if isinstance(seed_in, str):
74
+ seed_in = seed_in.strip()
75
+ seed = int(float(seed_in)) if seed_in != "" else -1
76
+ elif seed_in is None:
77
+ seed = -1
78
+ else:
79
+ seed = int(seed_in)
80
+ except Exception:
81
+ seed = -1
82
+
83
+ if seed >= 0:
84
+ generator = torch.Generator(device=DEVICE).manual_seed(seed)
85
+ else:
86
+ generator = torch.Generator(device=DEVICE)
87
+
88
+ # Prompt principal (on enrobe pour aider SDXL)
89
+ final_prompt = (
90
+ "masterpiece, best quality, highly detailed, "
91
+ + (prompt or "1girl, detailed face, realistic skin")
92
+ + ", sharp focus"
93
+ )
94
 
95
+ # Negative : "wrong" est la clé pour sdxl-wrong-lora [web:183][web:194]
96
+ if negative and negative.strip():
97
+ final_negative = "wrong, " + negative.strip()
98
  else:
99
+ final_negative = "wrong, blurry, low quality, deformed, bad anatomy, extra limbs"
 
100
 
101
+ # On combine les deux LoRA : WRONG à 1.0, face en slider
102
+ adapters = [LORA_WRONG_ADAPTER, LORA_FACE_ADAPTER]
103
+ weights = [1.0, float(face_weight)]
104
+ pipe.set_adapters(adapters, adapter_weights=weights)
105
 
106
+ try:
107
+ result = pipe(
108
+ prompt=final_prompt,
109
+ negative_prompt=final_negative,
110
+ num_inference_steps=int(steps),
111
+ guidance_scale=float(guidance),
112
+ width=int(width),
113
+ height=int(height),
114
+ generator=generator,
115
+ )
116
+ except Exception as e:
117
+ return None, f"Erreur pendant la génération : {repr(e)}", ""
118
 
119
+ image = result.images[0]
 
 
 
 
 
 
 
 
120
 
121
+ metadata = {
122
+ "base_model": BASE_MODEL_ID,
123
+ "prompt_raw": prompt,
124
+ "prompt_final": final_prompt,
125
+ "negative_raw": negative,
126
+ "negative_final": final_negative,
127
+ "seed": seed,
128
+ "steps": int(steps),
129
+ "guidance": float(guidance),
130
+ "width": int(width),
131
+ "height": int(height),
132
+ "lora_wrong_repo": LORA_WRONG_REPO,
133
+ "lora_wrong_adapter": LORA_WRONG_ADAPTER,
134
+ "lora_wrong_weight": 1.0,
135
+ "lora_face_repo": LORA_FACE_REPO,
136
+ "lora_face_adapter": LORA_FACE_ADAPTER,
137
+ "lora_face_weight": float(face_weight),
138
+ }
139
+
140
+ base_name = script_name.strip().replace(" ", "_") if script_name else "sdxl_wrong_lora"
141
+ img_path = os.path.join("outputs", f"{base_name}.png")
142
+ json_path = os.path.join("outputs", f"{base_name}.json")
143
 
 
 
144
  pnginfo = PngImagePlugin.PngInfo()
145
+ pnginfo.add_text("generation_params", json.dumps(metadata, ensure_ascii=False))
146
+ image.save(img_path, pnginfo=pnginfo)
147
+
148
+ with open(json_path, "w", encoding="utf-8") as f:
149
+ json.dump(metadata, f, ensure_ascii=False, indent=2)
150
 
151
+ script_txt = json.dumps(metadata, ensure_ascii=False, indent=2)
152
+ return image, script_txt, json_path
153
 
 
154
 
155
+ # -----------------------
156
+ # UI Gradio
157
+ # -----------------------
158
+ with gr.Blocks(title="SDXL + WRONG LoRA + Face Detail") as demo:
159
+ gr.Markdown(
160
+ "## SDXL 1.0 + LoRA **WRONG** (meilleure compréhension) + LoRA détail visage \n"
161
+ "- Utilise le LoRA `sdxl-wrong-lora` pour améliorer qualité et adhérence au prompt.[web:183][web:194]\n"
162
+ "- Utilise `Detailers_By_Stable_Yogi` pour les visages/détails.[web:60][web:63]\n"
163
+ "- NSFW débloqué (safety checker bypassé)."
164
+ )
165
+
166
  with gr.Row():
167
  with gr.Column():
168
  prompt = gr.Textbox(
169
+ label="Prompt",
170
+ placeholder="1girl nude, red dress, on the beach at sunset, cinematic lighting, smiling at viewer",
171
+ value=None,
172
  lines=4,
 
173
  )
174
  negative = gr.Textbox(
175
+ label="Negative prompt (\"wrong\" sera ajouté automatiquement)",
176
+ placeholder="blurry, deformed, ugly, extra limbs, bad anatomy",
177
+ value=None,
178
+ lines=3,
179
+ )
180
+
181
+ seed = gr.Number(
182
+ label="Seed (-1 = random)",
183
+ value=-1,
184
  )
185
+ steps = gr.Slider(
186
+ minimum=20,
187
+ maximum=60,
188
+ value=40,
189
+ step=1,
190
+ label="Steps (plus haut = meilleure adhérence)",
191
+ )
192
+ guidance = gr.Slider(
193
+ minimum=5.0,
194
+ maximum=15.0,
195
+ value=9.0,
196
+ step=0.5,
197
+ label="CFG / Guidance scale",
198
+ )
199
+
200
+ width = gr.Slider(
201
+ minimum=512,
202
+ maximum=1536,
203
+ value=1024,
204
+ step=64,
205
+ label="Width (SDXL natif 1024)",
206
+ )
207
+ height = gr.Slider(
208
+ minimum=512,
209
+ maximum=1536,
210
+ value=1024,
211
+ step=64,
212
+ label="Height (SDXL natif 1024)",
213
+ )
214
+
215
+ face_weight = gr.Slider(
216
+ minimum=0.0,
217
+ maximum=1.2,
218
+ value=0.7,
219
+ step=0.05,
220
+ label="Force LoRA face/detail (Stable Yogi)",
221
+ )
222
+
223
+ script_name = gr.Textbox(
224
+ label="Nom base pour l'image / script",
225
+ value="sdxl_wrong_example",
226
+ )
227
+
228
+ run_btn = gr.Button("🚀 Générer", variant="primary")
229
+
230
  with gr.Column():
231
+ out_img = gr.Image(
232
+ label="Image générée (SDXL + WRONG + Face)",
 
 
 
 
 
 
233
  )
234
+ out_script = gr.Textbox(
235
+ label="Metadata / Script JSON",
236
+ lines=20,
237
+ )
238
+ out_file = gr.File(
239
+ label="Fichier JSON des paramètres (téléchargeable)",
240
+ )
241
+
242
+ run_btn.click(
243
+ fn=generate,
244
+ inputs=[
245
+ prompt,
246
+ negative,
247
+ seed,
248
+ steps,
249
+ guidance,
250
+ width,
251
+ height,
252
+ face_weight,
253
+ script_name,
254
+ ],
255
+ outputs=[out_img, out_script, out_file],
256
+ )
257
 
258
+ if __name__ == "__main__":
259
+ demo.launch()