alexander00001 commited on
Commit
3bf5836
·
verified ·
1 Parent(s): 9e960d8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -626
app.py CHANGED
@@ -1,81 +1,11 @@
1
- # ===== 必须首先导入spaces =====
2
- try:
3
- import spaces
4
- SPACES_AVAILABLE = True
5
- print("✅ Spaces available - ZeroGPU mode")
6
- except ImportError:
7
- SPACES_AVAILABLE = False
8
- print("⚠️ Spaces not available - running in regular mode")
9
-
10
- # ===== 其他导入 =====
11
- import os
12
- from datetime import datetime
13
- import random
14
- import torch
15
- import gradio as gr
16
- from diffusers import AutoPipelineForText2Image, FlowMatchEulerDiscreteScheduler
17
- from PIL import Image
18
- import traceback
19
- import numpy as np
20
- import gc # 添加垃圾回收
21
-
22
- # 移除 Compel(FLUX 不兼容,简化处理)
23
- COMPEL_AVAILABLE = False
24
- print("⚠️ Compel disabled for FLUX compatibility")
25
-
26
- # ===== 简化后的配置 =====
27
- STYLE_PRESETS = {
28
- "None": "",
29
- "Realistic": "photorealistic, 8k, ultra-detailed, cinematic lighting, masterpiece, realistic skin texture, detailed anatomy",
30
- "Anime": "anime style, detailed, high quality, masterpiece, best quality, detailed eyes, perfect anatomy",
31
- "Comic": "comic book style, bold outlines, vibrant colors, cel shading, dynamic pose",
32
- "Watercolor": "watercolor illustration, soft gradients, pastel palette, artistic brush strokes"
33
- }
34
-
35
- # 固定模型配置
36
- FIXED_MODEL = "aoxo/flux.1dev-abliterated"
37
-
38
- # 质量增强提示词(适配 NSFW)
39
- QUALITY_ENHANCERS = [
40
- "detailed anatomy", "(perfect anatomy:1.2)", "soft skin", "natural lighting",
41
- "high resolution", "(masterpiece:1.3)", "(best quality:1.2)",
42
- "professional photography", "artistic composition",
43
- "(perfect proportions:1.1)", "smooth textures", "intimate lighting",
44
- "realistic skin texture", "(detailed face:1.1)", "natural pose"
45
- ]
46
-
47
- # 风格专用增强词
48
- STYLE_ENHANCERS = {
49
- "Realistic": ["photorealistic", "(ultra realistic:1.2)", "natural lighting", "detailed skin", "professional photography"],
50
- "Anime": ["anime style", "(high quality anime:1.2)", "detailed eyes", "perfect face", "clean art style"],
51
- "Comic": ["comic book style", "bold outlines", "vibrant colors", "cel shading"],
52
- "Watercolor": ["watercolor style", "artistic", "soft gradients", "pastel palette"]
53
- }
54
-
55
- SAVE_DIR = "generated_images"
56
- os.makedirs(SAVE_DIR, exist_ok=True)
57
-
58
- # ===== 模型相关变量 =====
59
- pipeline = None
60
- device = None
61
- model_loaded = False
62
-
63
- def cleanup_memory():
64
- """清理GPU内存"""
65
- if torch.cuda.is_available():
66
- torch.cuda.empty_cache()
67
- torch.cuda.synchronize()
68
- gc.collect()
69
-
70
  def initialize_model():
71
- """优化的模型初始化函数(针对ZeroGPU优化)"""
72
  global pipeline, device, model_loaded
73
 
74
  if model_loaded and pipeline is not None:
75
  return True
76
 
77
  try:
78
- # 清理内存
79
  cleanup_memory()
80
 
81
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
@@ -83,30 +13,29 @@ def initialize_model():
83
 
84
  print(f"📦 Loading fixed model: {FIXED_MODEL}")
85
 
86
- # ZeroGPU优化的模型加载
87
  pipeline = AutoPipelineForText2Image.from_pretrained(
88
  FIXED_MODEL,
89
- torch_dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
90
- # 添加内存优化参数
91
- variant=None,
92
- use_safetensors=True
93
  )
94
 
95
- # 优化调度器(默认 FlowMatchEulerDiscreteScheduler)
96
  pipeline.scheduler = FlowMatchEulerDiscreteScheduler.from_config(
97
  pipeline.scheduler.config
98
  )
99
  pipeline = pipeline.to(device)
100
 
101
- # ZeroGPU专用优化
102
  if torch.cuda.is_available():
103
- # 使用更保守的内存设置
104
- pipeline.enable_model_cpu_offload() # 改用model_cpu_offload
105
  pipeline.enable_vae_slicing()
106
- pipeline.enable_vae_tiling() # 添加VAE tiling
 
 
 
107
 
108
- # 移除 torch.compile(Spaces 不稳定)
109
- print("✅ Model initialization complete (ZeroGPU optimized)")
110
  model_loaded = True
111
  return True
112
 
@@ -117,72 +46,22 @@ def initialize_model():
117
  model_loaded = False
118
  return False
119
 
120
- def enhance_prompt(prompt: str, style: str) -> str:
121
- """增强提示词"""
122
- # 限制质量词数量,避免过长
123
- quality_terms = ", ".join(QUALITY_ENHANCERS[:5]) # 只取前5个
124
-
125
- style_terms = ""
126
- if style in STYLE_ENHANCERS:
127
- style_terms = ", " + ", ".join(STYLE_ENHANCERS[style][:3]) # 只取前3个
128
-
129
- style_suffix = STYLE_PRESETS.get(style, "")
130
-
131
- enhanced_parts = [prompt.strip()]
132
-
133
- if style_suffix:
134
- enhanced_parts.append(style_suffix)
135
-
136
- if style_terms:
137
- enhanced_parts.append(style_terms.lstrip(", "))
138
-
139
- enhanced_parts.append(quality_terms)
140
-
141
- enhanced_prompt = ", ".join(filter(None, enhanced_parts))
142
-
143
- # 限制总长度,避免超出模型限制
144
- if len(enhanced_prompt) > 500:
145
- enhanced_prompt = enhanced_prompt[:500] + "..."
146
-
147
- return enhanced_prompt
148
-
149
- def apply_spaces_decorator(func):
150
- """应用 spaces 装饰器,增加更长的超时时间"""
151
- if SPACES_AVAILABLE:
152
- # 增加超时时间到120秒,并设置更大的内存限制
153
- return spaces.GPU(duration=120)(func)
154
- return func
155
-
156
- def create_metadata_content(prompt, enhanced_prompt, seed, steps, cfg_scale, width, height, style):
157
- """创建元数据内容"""
158
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
159
- return f"""Generated Image Metadata
160
- ======================
161
- Timestamp: {timestamp}
162
- Original Prompt: {prompt}
163
- Enhanced Prompt: {enhanced_prompt}
164
- Seed: {seed}
165
- Steps: {steps}
166
- CFG Scale: {cfg_scale}
167
- Dimensions: {width}x{height}
168
- Style: {style}
169
- Model: FLUX.1-dev
170
- """
171
 
172
  @apply_spaces_decorator
173
- def generate_image(prompt: str, style: str, negative_prompt: str = "", steps: int = 20, cfg_scale: float = 3.5,
174
- seed: int = -1, width: int = 1024, height: int = 1024, progress=gr.Progress()):
175
- """图像生成函数(ZeroGPU优化版本)"""
 
 
176
  try:
177
  if not prompt or prompt.strip() == "":
178
  return None, "", "❌ Please enter a prompt"
179
 
180
- # 参数验证和限制
181
- steps = max(10, min(steps, 30)) # 限制步数范围,避免超时
182
- width = min(width, 1024) # 限制最大尺寸
183
  height = min(height, 1024)
184
 
185
- # 初始化模型
186
  progress(0.1, desc="Initializing model...")
187
  if not initialize_model():
188
  cleanup_memory()
@@ -190,28 +69,24 @@ def generate_image(prompt: str, style: str, negative_prompt: str = "", steps: in
190
 
191
  progress(0.2, desc="Processing prompt...")
192
 
193
- # 处理 seed
194
  if seed == -1:
195
  seed = random.randint(0, np.iinfo(np.int32).max)
196
 
197
- # 增强提示词
198
  enhanced_prompt = enhance_prompt(prompt.strip(), style)
199
 
200
- # 增强负面提示词
201
  if not negative_prompt.strip():
202
- negative_prompt = "(low quality, worst quality:1.4), (bad anatomy, bad hands:1.2), blurry, watermark, signature, text, error, missing limbs, extra limbs, cropped, normal quality, jpeg artifacts, deformed, mutated"
203
 
204
- # 生成参数(官方示例:generator 用 cpu)
205
  generator = torch.Generator("cpu").manual_seed(seed)
206
 
207
  progress(0.4, desc="Starting generation...")
208
- print(f"🔥 Starting inference: steps={steps}, guidance={cfg_scale}, size={width}x{height}")
209
 
210
- # 清理内存
211
  cleanup_memory()
212
 
213
- # 直接使用标准 pipeline(无 Compel,添加 max_sequence_length
214
- with torch.no_grad(): # 确保不计算梯度
215
  result = pipeline(
216
  prompt=enhanced_prompt,
217
  negative_prompt=negative_prompt,
@@ -219,7 +94,7 @@ def generate_image(prompt: str, style: str, negative_prompt: str = "", steps: in
219
  guidance_scale=cfg_scale,
220
  width=width,
221
  height=height,
222
- max_sequence_length=256, # 减少序列长度,节省内存
223
  generator=generator,
224
  output_type="pil"
225
  )
@@ -229,30 +104,26 @@ def generate_image(prompt: str, style: str, negative_prompt: str = "", steps: in
229
 
230
  progress(0.9, desc="Finalizing...")
231
 
232
- # 立即清理内存
233
  del result
234
  cleanup_memory()
235
 
236
- # 保存图像
237
  filename = f"IMG_{seed}.png"
238
  filepath = os.path.join(SAVE_DIR, filename)
239
  image.save(filepath, format="PNG", optimize=True)
240
 
241
- # 创建元数据内容
242
  metadata_content = create_metadata_content(
243
  prompt, enhanced_prompt, seed, steps, cfg_scale, width, height, style
244
  )
245
 
246
  progress(1.0, desc="Complete!")
247
 
248
- # 生成信息显示
249
  generation_info = f"Prompt: {prompt}\nSeed: {seed} | Size: {width}×{height} | Steps: {steps} | CFG: {cfg_scale}"
250
 
251
  return image, generation_info, metadata_content
252
 
253
  except torch.cuda.OutOfMemoryError as e:
254
  cleanup_memory()
255
- error_msg = "❌ GPU memory insufficient. Try reducing image size or steps."
256
  print(f"CUDA OOM: {error_msg}")
257
  return None, "", error_msg
258
 
@@ -263,478 +134,49 @@ def generate_image(prompt: str, style: str, negative_prompt: str = "", steps: in
263
  print(traceback.format_exc())
264
  return None, "", f"❌ Generation failed: {error_msg}"
265
 
266
- # ===== CSS 样式(保持不变)=====
267
- css = """
268
- /* 全局容器 */
269
- .gradio-container {
270
- max-width: 100% !important;
271
- margin: 0 !important;
272
- padding: 0 !important;
273
- background: linear-gradient(135deg, #e6a4f2 0%, #1197e4 100%) !important;
274
- min-height: 100vh !important;
275
- font-family: 'Segoe UI', Arial, sans-serif !important;
276
- }
277
-
278
- /* 主要内容区域 */
279
- .main-content {
280
- background: rgba(255, 255, 255, 0.95) !important;
281
- border-radius: 20px !important;
282
- padding: 20px !important;
283
- margin: 15px !important;
284
- box-shadow: 0 10px 25px rgba(0,0,0,0.2) !important;
285
- min-height: calc(100vh - 30px) !important;
286
- color: #3e3e3e !important;
287
- backdrop-filter: blur(10px) !important;
288
- }
289
-
290
- /* 简化标题 */
291
- .title {
292
- text-align: center !important;
293
- background: linear-gradient(45deg, #bb6ded, #08676b) !important;
294
- -webkit-background-clip: text !important;
295
- -webkit-text-fill-color: transparent !important;
296
- background-clip: text !important;
297
- font-size: 2rem !important;
298
- margin-bottom: 15px !important;
299
- font-weight: bold !important;
300
- }
301
-
302
- /* 简化警告信息 */
303
- .warning-box {
304
- background: linear-gradient(45deg, #bb6ded, #08676b) !important;
305
- color: white !important;
306
- padding: 8px !important;
307
- border-radius: 8px !important;
308
- margin-bottom: 15px !important;
309
- text-align: center !important;
310
- font-weight: bold !important;
311
- font-size: 14px !important;
312
- }
313
-
314
- /* 输入框样式 */
315
- .prompt-box textarea, .prompt-box input {
316
- border-radius: 10px !important;
317
- border: 2px solid #bb6ded !important;
318
- padding: 15px !important;
319
- font-size: 14px !important;
320
- background: linear-gradient(135deg, rgba(245, 243, 255, 0.9), rgba(237, 233, 254, 0.9)) !important;
321
- color: #2d2d2d !important;
322
- }
323
-
324
- .prompt-box textarea:focus, .prompt-box input:focus {
325
- border-color: #08676b !important;
326
- box-shadow: 0 0 15px rgba(77, 8, 161, 0.3) !important;
327
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(248, 249, 250, 0.95)) !important;
328
- }
329
-
330
- /* 右侧控制区域 */
331
- .controls-section {
332
- background: linear-gradient(135deg, rgba(224, 218, 255, 0.8), rgba(196, 181, 253, 0.8)) !important;
333
- border-radius: 12px !important;
334
- padding: 15px !important;
335
- margin-bottom: 10px !important;
336
- border: 2px solid rgba(187, 109, 237, 0.3) !important;
337
- backdrop-filter: blur(5px) !important;
338
- }
339
-
340
- .controls-section label {
341
- font-weight: 600 !important;
342
- color: #2d2d2d !important;
343
- margin-bottom: 8px !important;
344
- }
345
-
346
- .controls-section input[type="radio"] {
347
- accent-color: #bb6ded !important;
348
- }
349
-
350
- .controls-section input[type="number"],
351
- .controls-section input[type="range"] {
352
- background: rgba(255, 255, 255, 0.9) !important;
353
- border: 1px solid #bb6ded !important;
354
- border-radius: 6px !important;
355
- padding: 8px !important;
356
- color: #2d2d2d !important;
357
- }
358
-
359
- .controls-section select {
360
- background: rgba(255, 255, 255, 0.9) !important;
361
- border: 1px solid #bb6ded !important;
362
- border-radius: 6px !important;
363
- padding: 8px !important;
364
- color: #2d2d2d !important;
365
- }
366
-
367
- /* 生成按钮 */
368
- .generate-btn {
369
- background: linear-gradient(45deg, #bb6ded, #08676b) !important;
370
- color: white !important;
371
- border: none !important;
372
- padding: 15px 25px !important;
373
- border-radius: 25px !important;
374
- font-size: 16px !important;
375
- font-weight: bold !important;
376
- width: 100% !important;
377
- cursor: pointer !important;
378
- transition: all 0.3s ease !important;
379
- text-transform: uppercase !important;
380
- letter-spacing: 1px !important;
381
- }
382
-
383
- .generate-btn:hover {
384
- transform: translateY(-2px) !important;
385
- box-shadow: 0 8px 25px rgba(187, 109, 237, 0.5) !important;
386
- }
387
-
388
- /* 图片输出区域 */
389
- .image-output {
390
- border-radius: 15px !important;
391
- overflow: hidden !important;
392
- max-width: 100% !important;
393
- max-height: 70vh !important;
394
- border: 3px solid #08676b !important;
395
- box-shadow: 0 8px 20px rgba(0,0,0,0.15) !important;
396
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(248, 249, 250, 0.9)) !important;
397
- }
398
-
399
- /* 图片信息区域 */
400
- .image-info {
401
- background: linear-gradient(135deg, rgba(248, 249, 250, 0.9), rgba(233, 236, 239, 0.9)) !important;
402
- border-radius: 8px !important;
403
- padding: 12px !important;
404
- margin-top: 10px !important;
405
- font-size: 12px !important;
406
- color: #495057 !important;
407
- border: 2px solid rgba(187, 109, 237, 0.2) !important;
408
- backdrop-filter: blur(5px) !important;
409
- }
410
-
411
- /* 保存按钮 */
412
- .save-btn {
413
- background: linear-gradient(45deg, #28a745, #20c997) !important;
414
- color: white !important;
415
- border: none !important;
416
- padding: 10px 20px !important;
417
- border-radius: 8px !important;
418
- font-size: 13px !important;
419
- font-weight: 600 !important;
420
- cursor: pointer !important;
421
- margin-top: 8px !important;
422
- margin-right: 10px !important;
423
- transition: all 0.3s ease !important;
424
- }
425
 
426
- .save-btn:hover {
427
- background: linear-gradient(45deg, #218838, #1ea471) !important;
428
- transform: translateY(-1px) !important;
429
- box-shadow: 0 4px 12px rgba(40, 167, 69, 0.3) !important;
430
- }
431
-
432
- /* 滑块样式 */
433
- .slider-container input[type="range"] {
434
- accent-color: #bb6ded !important;
435
- }
436
-
437
- /* 响应式设计 */
438
- @media (max-width: 768px) {
439
- .main-content {
440
- margin: 10px !important;
441
- padding: 15px !important;
442
- }
443
 
444
- .title {
445
- font-size: 1.5rem !important;
446
- }
447
- }
448
-
449
- /* 隐藏占位符 */
450
- .gr-image .image-container:empty::before {
451
- content: "Generated image will appear here" !important;
452
- display: flex !important;
453
- align-items: center !important;
454
- justify-content: center !important;
455
- height: 300px !important;
456
- background: linear-gradient(135deg, rgba(248, 249, 250, 0.8), rgba(233, 236, 239, 0.8)) !important;
457
- border-radius: 10px !important;
458
- color: #6c757d !important;
459
- font-size: 16px !important;
460
- font-weight: 500 !important;
461
- backdrop-filter: blur(5px) !important;
462
- }
463
- """
464
-
465
- # ===== 创建 UI =====
466
- def create_interface():
467
- with gr.Blocks(css=css, title="NSFW FLUX Image Generator") as interface:
468
- with gr.Column(elem_classes=["main-content"]):
469
- # 简化标题
470
- gr.HTML('<div class="title">NSFW FLUX Image Generator</div>')
471
-
472
- # 简化警告信息
473
- gr.HTML('''
474
- <div class="warning-box">
475
- ⚠️ 18+ CONTENT WARNING ⚠️
476
- </div>
477
- ''')
478
-
479
- # 主要输入区域
480
- with gr.Row():
481
- # 左侧:提示词输入
482
- with gr.Column(scale=2):
483
- prompt_input = gr.Textbox(
484
- label="Main Prompt (Keep it concise for best results)",
485
- placeholder="beautiful woman, detailed portrait, realistic...",
486
- lines=8, # 减少行数,提示用户简洁
487
- elem_classes=["prompt-box"]
488
- )
489
-
490
- # 添加提示信息
491
- gr.HTML('''
492
- <div style="background: rgba(255, 193, 7, 0.1); padding: 10px; border-radius: 8px; margin: 10px 0; border-left: 4px solid #ffc107;">
493
- <small><strong>💡 Prompt Tips:</strong><br>
494
- • Keep prompts under 75 tokens (~15-20 words)<br>
495
- • Focus on main subject and key details<br>
496
- • Let the style preset handle quality terms</small>
497
- </div>
498
- ''')
499
-
500
- negative_prompt_input = gr.Textbox(
501
- label="Negative Prompt (Optional)",
502
- placeholder="low quality, blurry, deformed...",
503
- lines=3, # 减少行数
504
- elem_classes=["prompt-box"]
505
- )
506
-
507
- # 右侧:控制选项
508
- with gr.Column(scale=1):
509
- # Style 选项
510
- with gr.Group(elem_classes=["controls-section"]):
511
- style_input = gr.Radio(
512
- label="Style Preset",
513
- choices=list(STYLE_PRESETS.keys()),
514
- value="Realistic"
515
- )
516
-
517
- # Seed 选项
518
- with gr.Group(elem_classes=["controls-section"]):
519
- seed_input = gr.Number(
520
- label="Seed (-1 for random)",
521
- value=-1,
522
- precision=0
523
- )
524
-
525
- # 宽度选择(降低最大值)
526
- with gr.Group(elem_classes=["controls-section"]):
527
- width_input = gr.Slider(
528
- label="Width",
529
- minimum=512,
530
- maximum=1024, # 降低最大值
531
- value=1024,
532
- step=64
533
- )
534
-
535
- # 高度选择(降低最大值)
536
- with gr.Group(elem_classes=["controls-section"]):
537
- height_input = gr.Slider(
538
- label="Height",
539
- minimum=512,
540
- maximum=1024, # 降低最大值
541
- value=1024,
542
- step=64
543
- )
544
-
545
- # 高级参数(调整默认值)
546
- with gr.Group(elem_classes=["controls-section"]):
547
- steps_input = gr.Slider(
548
- label="Steps",
549
- minimum=10,
550
- maximum=30, # 降低最大值
551
- value=20, # 降低默认值
552
- step=1
553
- )
554
-
555
- cfg_input = gr.Slider(
556
- label="CFG Scale",
557
- minimum=1.0,
558
- maximum=15.0,
559
- value=3.5,
560
- step=0.1
561
- )
562
-
563
- # 生成按钮
564
- generate_button = gr.Button(
565
- "GENERATE",
566
- elem_classes=["generate-btn"],
567
- variant="primary"
568
- )
569
-
570
- # 图片输出区域
571
- image_output = gr.Image(
572
- label="Generated Image",
573
- elem_classes=["image-output"],
574
- show_label=False,
575
- container=True
576
- )
577
-
578
- # 图片信息和保存按钮
579
- generation_info = gr.Textbox(
580
- label="Generation Info",
581
- interactive=False,
582
- elem_classes=["image-info"],
583
- show_label=False,
584
- visible=False
585
- )
586
-
587
- # 隐藏的变量存储
588
- metadata_content = gr.Textbox(visible=False)
589
- current_seed = gr.Number(visible=False)
590
- current_image = gr.Image(visible=False)
591
-
592
- # 下载按钮区域
593
- with gr.Row(visible=False) as download_row:
594
- download_image_btn = gr.Button(
595
- "Save Image",
596
- elem_classes=["save-btn"],
597
- size="sm"
598
- )
599
-
600
- download_metadata_btn = gr.Button(
601
- "Save Metadata",
602
- elem_classes=["save-btn"],
603
- size="sm"
604
- )
605
-
606
- # 生成图片的主要函数
607
- def on_generate(prompt, style, neg_prompt, steps, cfg, seed, width, height):
608
- image, info, metadata = generate_image(
609
- prompt, style, neg_prompt, steps, cfg, seed, width, height
610
- )
611
-
612
- if image is not None:
613
- # 提取实际使用的 seed
614
- try:
615
- actual_seed = seed if seed != -1 else int(info.split("Seed:")[1].split("|")[0].strip())
616
- except:
617
- actual_seed = seed if seed != -1 else random.randint(0, 999999)
618
-
619
- return (
620
- image, # 图片输出
621
- info, # 生成信息
622
- metadata, # 元数据
623
- actual_seed, # 当前 seed
624
- image, # 当前图片副本
625
- gr.update(visible=True), # 显示生成信息
626
- gr.update(visible=True) # 显示下载按钮区域
627
- )
628
- else:
629
- return (
630
- None,
631
- info,
632
- "",
633
- 0,
634
- None,
635
- gr.update(visible=False),
636
- gr.update(visible=False)
637
- )
638
-
639
- # 创建下载文件的函数
640
- def create_download_image(image_data, seed_val):
641
- if image_data is not None:
642
- filename = f"IMG_{seed_val}.png"
643
- filepath = os.path.join(SAVE_DIR, filename)
644
- image_data.save(filepath, format="PNG", optimize=True)
645
- return filepath
646
- return None
647
-
648
- def create_download_metadata(metadata_text, seed_val):
649
- if metadata_text:
650
- filename = f"IMG_{seed_val}.txt"
651
- filepath = os.path.join(SAVE_DIR, filename)
652
- with open(filepath, 'w', encoding='utf-8') as f:
653
- f.write(metadata_text)
654
- return filepath
655
- return None
656
-
657
- # 绑定生成事件
658
- generate_button.click(
659
- fn=on_generate,
660
- inputs=[
661
- prompt_input, style_input, negative_prompt_input,
662
- steps_input, cfg_input, seed_input, width_input, height_input
663
- ],
664
- outputs=[
665
- image_output, generation_info, metadata_content,
666
- current_seed, current_image, generation_info, download_row
667
- ],
668
- show_progress=True
669
- )
670
-
671
- # 支持 Enter 键触发
672
- prompt_input.submit(
673
- fn=on_generate,
674
- inputs=[
675
- prompt_input, style_input, negative_prompt_input,
676
- steps_input, cfg_input, seed_input, width_input, height_input
677
- ],
678
- outputs=[
679
- image_output, generation_info, metadata_content,
680
- current_seed, current_image, generation_info, download_row
681
- ],
682
- show_progress=True
683
- )
684
-
685
- # 下载图片
686
- def handle_image_download(image_data, seed_val):
687
- filepath = create_download_image(image_data, seed_val)
688
- if filepath:
689
- return gr.File(value=filepath, visible=True)
690
- return gr.File(visible=False)
691
-
692
- download_image_btn.click(
693
- fn=handle_image_download,
694
- inputs=[current_image, current_seed],
695
- outputs=[gr.File()]
696
- )
697
-
698
- # 下载元数据
699
- def handle_metadata_download(metadata_text, seed_val):
700
- filepath = create_download_metadata(metadata_text, seed_val)
701
- if filepath:
702
- return gr.File(value=filepath, visible=True)
703
- return gr.File(visible=False)
704
-
705
- download_metadata_btn.click(
706
- fn=handle_metadata_download,
707
- inputs=[metadata_content, current_seed],
708
- outputs=[gr.File()]
709
- )
710
-
711
- # 启动时显示欢迎信息
712
- interface.load(
713
- fn=lambda: (
714
- None, "", "", 0, None,
715
- gr.update(visible=False),
716
- gr.update(visible=False)
717
- ),
718
- outputs=[
719
- image_output, generation_info, metadata_content,
720
- current_seed, current_image, generation_info, download_row
721
- ]
722
- )
723
 
724
- return interface
725
-
726
- # ===== 启动应用 =====
727
- if __name__ == "__main__":
728
- print("🎨 Starting NSFW FLUX Image Generator...")
729
- print(f"🔧 Fixed Model: {FIXED_MODEL}")
730
- print(f"🔧 CUDA: {'✅ Available' if torch.cuda.is_available() else '❌ Not Available'}")
 
 
 
 
 
 
731
 
732
- app = create_interface()
733
- app.queue(max_size=5, default_concurrency_limit=1) # 降低并发限制
 
734
 
735
- app.launch(
736
- server_name="0.0.0.0",
737
- server_port=7860,
738
- show_error=True,
739
- share=False
740
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  def initialize_model():
2
+ """优化后的模型初始化函数"""
3
  global pipeline, device, model_loaded
4
 
5
  if model_loaded and pipeline is not None:
6
  return True
7
 
8
  try:
 
9
  cleanup_memory()
10
 
11
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
 
13
 
14
  print(f"📦 Loading fixed model: {FIXED_MODEL}")
15
 
16
+ # 优化的模型加载
17
  pipeline = AutoPipelineForText2Image.from_pretrained(
18
  FIXED_MODEL,
19
+ torch_dtype=torch.bfloat16,
20
+ use_safetensors=True,
21
+ variant=None
 
22
  )
23
 
 
24
  pipeline.scheduler = FlowMatchEulerDiscreteScheduler.from_config(
25
  pipeline.scheduler.config
26
  )
27
  pipeline = pipeline.to(device)
28
 
29
+ # 关键优化:使用sequential而不是model cpu offload
30
  if torch.cuda.is_available():
31
+ pipeline.to(device) # 改这里!
 
32
  pipeline.enable_vae_slicing()
33
+ pipeline.enable_vae_tiling()
34
+
35
+ # 可选:如果内存充足,可以完全不用offload
36
+ # pipeline.to(device) # 全部加载到GPU,最快但吃内存
37
 
38
+ print("✅ Model initialization complete (Optimized)")
 
39
  model_loaded = True
40
  return True
41
 
 
46
  model_loaded = False
47
  return False
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  @apply_spaces_decorator
51
+ def generate_image(prompt: str, style: str, negative_prompt: str = "",
52
+ steps: int = 20, cfg_scale: float = 3.5,
53
+ seed: int = -1, width: int = 1024, height: int = 1024,
54
+ progress=gr.Progress()):
55
+ """优化后的图像生成函数"""
56
  try:
57
  if not prompt or prompt.strip() == "":
58
  return None, "", "❌ Please enter a prompt"
59
 
60
+ # 优化的参数限制
61
+ steps = max(10, min(steps, 25)) # 降低最大步数
62
+ width = min(width, 1024)
63
  height = min(height, 1024)
64
 
 
65
  progress(0.1, desc="Initializing model...")
66
  if not initialize_model():
67
  cleanup_memory()
 
69
 
70
  progress(0.2, desc="Processing prompt...")
71
 
 
72
  if seed == -1:
73
  seed = random.randint(0, np.iinfo(np.int32).max)
74
 
75
+ # 不要过度增强提示词
76
  enhanced_prompt = enhance_prompt(prompt.strip(), style)
77
 
 
78
  if not negative_prompt.strip():
79
+ negative_prompt = "(low quality, worst quality:1.4), blurry, deformed"
80
 
 
81
  generator = torch.Generator("cpu").manual_seed(seed)
82
 
83
  progress(0.4, desc="Starting generation...")
84
+ print(f"🔥 Inference: steps={steps}, guidance={cfg_scale}, size={width}x{height}")
85
 
 
86
  cleanup_memory()
87
 
88
+ # 关键改动:提高max_sequence_length
89
+ with torch.no_grad():
90
  result = pipeline(
91
  prompt=enhanced_prompt,
92
  negative_prompt=negative_prompt,
 
94
  guidance_scale=cfg_scale,
95
  width=width,
96
  height=height,
97
+ max_sequence_length=512, # 从256改到512!
98
  generator=generator,
99
  output_type="pil"
100
  )
 
104
 
105
  progress(0.9, desc="Finalizing...")
106
 
 
107
  del result
108
  cleanup_memory()
109
 
 
110
  filename = f"IMG_{seed}.png"
111
  filepath = os.path.join(SAVE_DIR, filename)
112
  image.save(filepath, format="PNG", optimize=True)
113
 
 
114
  metadata_content = create_metadata_content(
115
  prompt, enhanced_prompt, seed, steps, cfg_scale, width, height, style
116
  )
117
 
118
  progress(1.0, desc="Complete!")
119
 
 
120
  generation_info = f"Prompt: {prompt}\nSeed: {seed} | Size: {width}×{height} | Steps: {steps} | CFG: {cfg_scale}"
121
 
122
  return image, generation_info, metadata_content
123
 
124
  except torch.cuda.OutOfMemoryError as e:
125
  cleanup_memory()
126
+ error_msg = "❌ GPU memory insufficient. Try: 768x768 or fewer steps"
127
  print(f"CUDA OOM: {error_msg}")
128
  return None, "", error_msg
129
 
 
134
  print(traceback.format_exc())
135
  return None, "", f"❌ Generation failed: {error_msg}"
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
+ def enhance_prompt(prompt: str, style: str) -> str:
139
+ """优化的提示词增强"""
140
+ # 减少质量词,避免提示词过长
141
+ quality_terms = ", ".join(QUALITY_ENHANCERS[:3]) # 从5减到3
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
+ style_terms = ""
144
+ if style in STYLE_ENHANCERS:
145
+ style_terms = ", " + ", ".join(STYLE_ENHANCERS[style][:2]) # 从3减到2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
+ style_suffix = STYLE_PRESETS.get(style, "")
148
+
149
+ enhanced_parts = [prompt.strip()]
150
+
151
+ if style_suffix:
152
+ enhanced_parts.append(style_suffix)
153
+
154
+ if style_terms:
155
+ enhanced_parts.append(style_terms.lstrip(", "))
156
+
157
+ enhanced_parts.append(quality_terms)
158
+
159
+ enhanced_prompt = ", ".join(filter(None, enhanced_parts))
160
 
161
+ # 提高长度限制,但不要太长
162
+ if len(enhanced_prompt) > 800: # 从500提高到800
163
+ enhanced_prompt = enhanced_prompt[:800]
164
 
165
+ return enhanced_prompt
166
+
167
+
168
+ # UI中的步数滑块也要调整
169
+ steps_input = gr.Slider(
170
+ label="Steps (15-20 recommended for speed)",
171
+ minimum=10,
172
+ maximum=25, # 从30降到25
173
+ value=15, # 从20降到15
174
+ step=1
175
+ )
176
+
177
+ # 添加尺寸预设方便快速选择
178
+ size_preset = gr.Radio(
179
+ label="Size Preset (smaller = faster)",
180
+ choices=["768x768 (Fast)", "1024x1024 (Quality)"],
181
+ value="768x768 (Fast)"
182
+ )