Spaces:
Running
on
Zero
Running
on
Zero
# ===== 必须首先导入spaces ===== | |
try: | |
import spaces | |
SPACES_AVAILABLE = True | |
print("✅ Spaces available - ZeroGPU mode") | |
except ImportError: | |
SPACES_AVAILABLE = False | |
print("⚠️ Spaces not available - running in regular mode") | |
# ===== 其他导入 ===== | |
import os | |
import uuid | |
from datetime import datetime | |
import random | |
import torch | |
import gradio as gr | |
from diffusers import StableDiffusionXLPipeline, EulerDiscreteScheduler | |
from PIL import Image | |
import traceback | |
import numpy as np | |
import io | |
import base64 | |
# ===== 长提示词处理 ===== | |
try: | |
from compel import Compel, ReturnedEmbeddingsType | |
COMPEL_AVAILABLE = True | |
print("✅ Compel available for long prompt processing") | |
except ImportError: | |
COMPEL_AVAILABLE = False | |
print("⚠️ Compel not available - using standard prompt processing") | |
# ===== 修复后的配置 ===== | |
STYLE_PRESETS = { | |
"None": "", | |
"Realistic": "photorealistic, 8k, ultra-detailed, cinematic lighting, masterpiece, realistic skin texture, detailed anatomy, professional photography", | |
"Anime": "anime style, detailed, high quality, masterpiece, best quality, detailed eyes, perfect anatomy, vibrant colors, clean art style", | |
"Comic": "comic book style, bold outlines, vibrant colors, cel shading, dynamic pose, graphic illustration", | |
"Watercolor": "watercolor illustration, soft gradients, pastel palette, artistic brush strokes, delicate textures" | |
} | |
# 固定模型配置 | |
FIXED_MODEL = "votepurchase/pornmasterPro_noobV3VAE" | |
# 固定LoRA配置 - 只保留Quality | |
QUALITY_LORA = { | |
"repo_id": "artificialguybr/LogoRedmond-LogoLoraForSDXL-V2", | |
"filename": "LogoRedAF.safetensors", | |
"scale": 0.8, | |
"description": "Quality and realism enhancer" | |
} | |
# 质量增强提示词 | |
QUALITY_ENHANCERS = [ | |
"detailed anatomy", "(perfect anatomy:1.2)", "soft skin", "natural lighting", | |
"high resolution", "(masterpiece:1.3)", "(best quality:1.2)", | |
"professional photography", "artistic composition", | |
"(perfect proportions:1.1)", "smooth textures", "intimate lighting", | |
"realistic skin texture", "(detailed face:1.1)", "natural pose" | |
] | |
# 修复后的风格专用增强词 - 更明显的差异 | |
STYLE_ENHANCERS = { | |
"Realistic": [ | |
"photorealistic", "(ultra realistic:1.3)", "natural lighting", "detailed skin", | |
"professional photography", "(hyperrealistic:1.2)", "lifelike", "raw photo", | |
"cinematic lighting", "depth of field" | |
], | |
"Anime": [ | |
"anime style", "(high quality anime:1.3)", "detailed eyes", "perfect face", | |
"clean art style", "(anime art:1.2)", "cel animation", "vibrant anime colors", | |
"manga style", "Japanese animation" | |
], | |
"Comic": [ | |
"comic book style", "(comic art:1.3)", "bold outlines", "vibrant colors", | |
"cel shading", "graphic illustration", "pop art", "cartoon style", | |
"comic book illustration", "graphic novel art" | |
], | |
"Watercolor": [ | |
"watercolor style", "(watercolor painting:1.3)", "artistic", "soft gradients", | |
"pastel palette", "delicate textures", "paint bleeding", "artistic brush strokes", | |
"traditional art", "painted illustration" | |
] | |
} | |
SAVE_DIR = "generated_images" | |
os.makedirs(SAVE_DIR, exist_ok=True) | |
# ===== 模型相关变量 ===== | |
pipeline = None | |
compel_processor = None | |
device = None | |
model_loaded = False | |
lora_loaded = False | |
def initialize_model(): | |
"""优化的模型初始化函数""" | |
global pipeline, compel_processor, device, model_loaded, lora_loaded | |
if model_loaded and pipeline is not None: | |
return True | |
try: | |
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") | |
print(f"🖥️ Using device: {device}") | |
print(f"📦 Loading fixed model: {FIXED_MODEL}") | |
# 基础模型加载 | |
pipeline = StableDiffusionXLPipeline.from_pretrained( | |
FIXED_MODEL, | |
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32, | |
variant="fp16" if torch.cuda.is_available() else None, | |
use_safetensors=True, | |
safety_checker=None, | |
requires_safety_checker=False | |
) | |
# 优化调度器 | |
pipeline.scheduler = EulerDiscreteScheduler.from_config( | |
pipeline.scheduler.config, | |
timestep_spacing="trailing" | |
) | |
pipeline = pipeline.to(device) | |
# 统一数据类型 | |
if torch.cuda.is_available(): | |
pipeline.text_encoder.to(torch.float16) | |
pipeline.text_encoder_2.to(torch.float16) | |
pipeline.vae.to(torch.float16) | |
pipeline.unet.to(torch.float16) | |
# GPU优化 | |
if torch.cuda.is_available(): | |
try: | |
pipeline.enable_vae_slicing() | |
pipeline.enable_attention_slicing() | |
try: | |
pipeline.enable_xformers_memory_efficient_attention() | |
except: | |
pass | |
except Exception as mem_error: | |
print(f"⚠️ Memory optimization warning: {mem_error}") | |
# 加载Quality LoRA | |
try: | |
print("🔧 Loading Quality LoRA...") | |
pipeline.load_lora_weights( | |
QUALITY_LORA["repo_id"], | |
weight_name=QUALITY_LORA["filename"], | |
adapter_name="quality_enhancer" | |
) | |
pipeline.set_adapters(["quality_enhancer"], adapter_weights=[QUALITY_LORA["scale"]]) | |
lora_loaded = True | |
print("✅ Quality LoRA loaded successfully") | |
except Exception as lora_error: | |
print(f"⚠️ LoRA loading failed: {lora_error}") | |
lora_loaded = False | |
# 初始化Compel | |
if COMPEL_AVAILABLE: | |
try: | |
compel_processor = Compel( | |
tokenizer=[pipeline.tokenizer, pipeline.tokenizer_2], | |
text_encoder=[pipeline.text_encoder, pipeline.text_encoder_2], | |
returned_embeddings_type=ReturnedEmbeddingsType.PENULTIMATE_HIDDEN_STATES_NON_NORMALIZED, | |
requires_pooled=[False, True], | |
truncate_long_prompts=False | |
) | |
print("✅ Long prompt processor (Compel) initialized successfully") | |
except Exception as compel_error: | |
print(f"⚠️ Compel initialization failed: {compel_error}") | |
compel_processor = None | |
model_loaded = True | |
print("✅ Model initialization complete") | |
return True | |
except Exception as e: | |
print(f"❌ Critical model loading error: {e}") | |
print(traceback.format_exc()) | |
model_loaded = False | |
return False | |
def enhance_prompt(prompt: str, style: str) -> str: | |
"""修复后的增强提示词函数""" | |
if not prompt or prompt.strip() == "": | |
return "" | |
enhanced_parts = [prompt.strip()] | |
# 添加风格前缀 | |
style_prefix = STYLE_PRESETS.get(style, "") | |
if style_prefix and style != "None": | |
enhanced_parts.insert(0, style_prefix) | |
# 添加风格特定增强词 | |
if style in STYLE_ENHANCERS and style != "None": | |
style_terms = ", ".join(STYLE_ENHANCERS[style]) | |
enhanced_parts.append(style_terms) | |
# 添加质量增强词 | |
quality_terms = ", ".join(QUALITY_ENHANCERS) | |
enhanced_parts.append(quality_terms) | |
enhanced_prompt = ", ".join(filter(None, enhanced_parts)) | |
print(f"🎨 Style: {style}") | |
print(f"📝 Original prompt: {prompt[:100]}...") | |
print(f"✨ Enhanced prompt: {enhanced_prompt[:150]}...") | |
return enhanced_prompt | |
def process_long_prompt(prompt, negative_prompt=""): | |
"""处理长提示词""" | |
if not compel_processor: | |
return None, None | |
try: | |
conditioning, pooled = compel_processor([prompt, negative_prompt]) | |
return conditioning, pooled | |
except Exception as e: | |
print(f"Long prompt processing failed: {e}") | |
return None, None | |
def apply_spaces_decorator(func): | |
"""应用spaces装饰器""" | |
if SPACES_AVAILABLE: | |
return spaces.GPU(duration=60)(func) | |
return func | |
def create_metadata_content(prompt, enhanced_prompt, seed, steps, cfg_scale, width, height, style): | |
"""创建元数据内容""" | |
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") | |
return f"""Generated Image Metadata | |
====================== | |
Timestamp: {timestamp} | |
Original Prompt: {prompt} | |
Enhanced Prompt: {enhanced_prompt} | |
Seed: {seed} | |
Steps: {steps} | |
CFG Scale: {cfg_scale} | |
Dimensions: {width}x{height} | |
Style: {style} | |
Model: PornMasterPro | |
LoRA: Quality Enhancer (scale: 0.8) | |
""" | |
def convert_to_pil_image(image_data): | |
"""转换图像数据为PIL Image对象""" | |
if image_data is None: | |
return None | |
# 如果已经是PIL Image,直接返回 | |
if isinstance(image_data, Image.Image): | |
return image_data | |
# 如果是numpy数组,转换为PIL Image | |
if isinstance(image_data, np.ndarray): | |
# 确保数据类型正确 | |
if image_data.dtype != np.uint8: | |
if image_data.max() <= 1.0: | |
image_data = (image_data * 255).astype(np.uint8) | |
else: | |
image_data = image_data.astype(np.uint8) | |
# 转换为PIL Image | |
if len(image_data.shape) == 3: | |
return Image.fromarray(image_data, 'RGB') | |
elif len(image_data.shape) == 2: | |
return Image.fromarray(image_data, 'L') | |
return None | |
def generate_image(prompt: str, style: str, negative_prompt: str = "", steps: int = 28, cfg_scale: float = 7.0, | |
seed: int = -1, width: int = 1024, height: int = 1024, progress=gr.Progress()): | |
"""图像生成函数""" | |
if not prompt or prompt.strip() == "": | |
return None, "", "" | |
# 初始化模型 | |
progress(0.1, desc="Loading model...") | |
if not initialize_model(): | |
return None, "", "❌ Failed to load model" | |
progress(0.3, desc="Processing prompt...") | |
try: | |
# 处理seed | |
if seed == -1: | |
seed = random.randint(0, np.iinfo(np.int32).max) | |
# 增强提示词 - 确保风格生效 | |
enhanced_prompt = enhance_prompt(prompt.strip(), style) | |
# 增强负面提示词 | |
if not negative_prompt.strip(): | |
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" | |
# 生成参数 | |
generator = torch.Generator(device).manual_seed(seed) | |
progress(0.5, desc="Generating image...") | |
# 长提示词处理 | |
use_long_prompt = len(enhanced_prompt.split()) > 60 or len(enhanced_prompt) > 300 | |
if use_long_prompt and compel_processor: | |
conditioning, pooled = process_long_prompt(enhanced_prompt, negative_prompt) | |
if conditioning is not None: | |
result = pipeline( | |
prompt_embeds=conditioning[0:1], | |
pooled_prompt_embeds=pooled[0:1], | |
negative_prompt_embeds=conditioning[1:2], | |
negative_pooled_prompt_embeds=pooled[1:2], | |
num_inference_steps=steps, | |
guidance_scale=cfg_scale, | |
width=width, | |
height=height, | |
generator=generator | |
) | |
image = result.images[0] | |
else: | |
result = pipeline( | |
prompt=enhanced_prompt, | |
negative_prompt=negative_prompt, | |
num_inference_steps=steps, | |
guidance_scale=cfg_scale, | |
width=width, | |
height=height, | |
generator=generator | |
) | |
image = result.images[0] | |
else: | |
result = pipeline( | |
prompt=enhanced_prompt, | |
negative_prompt=negative_prompt, | |
num_inference_steps=steps, | |
guidance_scale=cfg_scale, | |
width=width, | |
height=height, | |
generator=generator | |
) | |
image = result.images[0] | |
progress(0.9, desc="Processing output...") | |
# 确保图像是PIL Image对象 | |
image = convert_to_pil_image(image) | |
if image is None: | |
return None, "", "❌ Failed to convert image" | |
# 创建元数据内容 | |
metadata_content = create_metadata_content( | |
prompt, enhanced_prompt, seed, steps, cfg_scale, width, height, style | |
) | |
progress(1.0, desc="Complete!") | |
# 生成信息显示 | |
generation_info = f"Style: {style} | Seed: {seed} | Size: {width}×{height} | Steps: {steps} | CFG: {cfg_scale}" | |
return image, generation_info, metadata_content | |
except Exception as e: | |
error_msg = str(e) | |
print(f"Generation error: {error_msg}") | |
print(traceback.format_exc()) | |
return None, "", f"❌ Generation failed: {error_msg}" | |
# ===== CSS样式 - 修复版 ===== | |
css = """ | |
/* 全局容器 */ | |
.gradio-container { | |
max-width: 100% !important; | |
margin: 0 !important; | |
padding: 0 !important; | |
background: linear-gradient(135deg, #e6a4f2 0%, #1197e4 100%) !important; | |
min-height: 100vh !important; | |
font-family: 'Segoe UI', Arial, sans-serif !important; | |
} | |
/* 主要内容区域 */ | |
.main-content { | |
background: rgba(255, 255, 255, 0.9) !important; | |
border-radius: 20px !important; | |
padding: 20px !important; | |
margin: 15px !important; | |
box-shadow: 0 10px 25px rgba(255, 255, 255, 0.2) !important; | |
min-height: calc(100vh - 30px) !important; | |
color: #3e3e3e !important; | |
backdrop-filter: blur(10px) !important; | |
} | |
/* 简化标题 */ | |
.title { | |
text-align: center !important; | |
background: linear-gradient(45deg, #bb6ded, #08676b) !important; | |
-webkit-background-clip: text !important; | |
-webkit-text-fill-color: transparent !important; | |
background-clip: text !important; | |
font-size: 2rem !important; | |
margin-bottom: 15px !important; | |
font-weight: bold !important; | |
} | |
/* 简化警告信息 */ | |
.warning-box { | |
background: linear-gradient(45deg, #bb6ded, #08676b) !important; | |
color: white !important; | |
padding: 8px !important; | |
border-radius: 8px !important; | |
margin-bottom: 15px !important; | |
text-align: center !important; | |
font-weight: bold !important; | |
font-size: 14px !important; | |
} | |
/* 输入框样式 - 修复背景色 */ | |
.prompt-box textarea, .prompt-box input { | |
border-radius: 10px !important; | |
border: 2px solid #bb6ded !important; | |
padding: 15px !important; | |
font-size: 18px !important; | |
background: linear-gradient(135deg, rgba(245, 243, 255, 0.9), rgba(237, 233, 254, 0.9)) !important; | |
color: #2d2d2d !important; | |
} | |
.prompt-box textarea:focus, .prompt-box input:focus { | |
border-color: #08676b !important; | |
box-shadow: 0 0 15px rgba(77, 8, 161, 0.3) !important; | |
background: linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(248, 249, 250, 0.95)) !important; | |
} | |
/* 右侧控制区域 - 修复背景色 */ | |
.controls-section { | |
background: linear-gradient(135deg, rgba(224, 218, 255, 0.8), rgba(196, 181, 253, 0.8)) !important; | |
border-radius: 12px !important; | |
padding: 15px !important; | |
margin-bottom: 8px !important; | |
border: 2px solid rgba(187, 109, 237, 0.3) !important; | |
backdrop-filter: blur(5px) !important; | |
} | |
.controls-section label { | |
font-weight: 600 !important; | |
color: #2d2d2d !important; | |
margin-bottom: 8px !important; | |
} | |
/* 修复单选按钮和输入框背景 */ | |
.controls-section input[type="radio"] { | |
accent-color: #bb6ded !important; | |
} | |
.controls-section input[type="number"], | |
.controls-section input[type="range"] { | |
background: rgba(255, 255, 255, 0.9) !important; | |
border: 1px solid #bb6ded !important; | |
border-radius: 6px !important; | |
padding: 8px !important; | |
color: #2d2d2d !important; | |
} | |
.controls-section select { | |
background: rgba(255, 255, 255, 0.9) !important; | |
border: 1px solid #bb6ded !important; | |
border-radius: 6px !important; | |
padding: 8px !important; | |
color: #2d2d2d !important; | |
} | |
/* 生成按钮 */ | |
.generate-btn { | |
background: linear-gradient(45deg, #bb6ded, #08676b) !important; | |
color: white !important; | |
border: none !important; | |
padding: 15px 25px !important; | |
border-radius: 25px !important; | |
font-size: 16px !important; | |
font-weight: bold !important; | |
width: 100% !important; | |
cursor: pointer !important; | |
transition: all 0.3s ease !important; | |
text-transform: uppercase !important; | |
letter-spacing: 1px !important; | |
} | |
.generate-btn:hover { | |
transform: translateY(-2px) !important; | |
box-shadow: 0 8px 25px rgba(187, 109, 237, 0.5) !important; | |
} | |
/* 图片输出区域 */ | |
.image-output { | |
border-radius: 15px !important; | |
overflow: hidden !important; | |
max-width: 100% !important; | |
max-height: 70vh !important; | |
border: 3px solid #08676b !important; | |
box-shadow: 0 8px 20px rgba(0,0,0,0.15) !important; | |
background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(248, 249, 250, 0.9)) !important; | |
} | |
/* 图片信息区域 */ | |
.image-info { | |
background: linear-gradient(135deg, rgba(248, 249, 250, 0.2), rgba(233, 236, 239, 0.9)) !important; | |
border-radius: 8px !important; | |
padding: 12px !important; | |
margin-top: 10px !important; | |
font-size: 12px !important; | |
color: #495057 !important; | |
border: 2px solid rgba(187, 109, 237, 0.2) !important; | |
backdrop-filter: blur(5px) !important; | |
} | |
/* 元数据区域样式 */ | |
.metadata-box { | |
background: linear-gradient(135deg, rgba(248, 249, 250, 0.2), rgba(233, 236, 239, 0.9)) !important; | |
border-radius: 8px !important; | |
padding: 15px !important; | |
margin-top: 15px !important; | |
font-family: 'Courier New', monospace !important; | |
font-size: 12px !important; | |
color: #495057 !important; | |
border: 2px solid rgba(187, 109, 237, 0.2) !important; | |
backdrop-filter: blur(5px) !important; | |
white-space: pre-wrap !important; | |
overflow-y: auto !important; | |
max-height: 300px !important; | |
} | |
/* 滑块样式 */ | |
.slider-container input[type="range"] { | |
accent-color: #bb6ded !important; | |
} | |
/* 响应式设计 */ | |
@media (max-width: 768px) { | |
.main-content { | |
margin: 10px !important; | |
padding: 15px !important; | |
} | |
.title { | |
font-size: 1.5rem !important; | |
} | |
} | |
/* 隐藏占位符 */ | |
.gr-image .image-container:empty::before { | |
content: "Generated image will appear here" !important; | |
display: flex !important; | |
align-items: center !important; | |
justify-content: center !important; | |
height: 300px !important; | |
background: linear-gradient(135deg, rgba(248, 249, 250, 0.8), rgba(233, 236, 239, 0.8)) !important; | |
border-radius: 10px !important; | |
color: #6c757d !important; | |
font-size: 16px !important; | |
font-weight: 500 !important; | |
backdrop-filter: blur(5px) !important; | |
/* 强制覆盖Gradio默认样式 */ | |
.gradio-container .gr-textbox, | |
.gradio-container .gr-radio-group, | |
.gradio-container .gr-slider, | |
.gradio-container .gr-number { | |
background: rgba(255, 255, 255, 0.95) !important; | |
border: 1px solid rgba(187, 109, 237, 0.5) !important; | |
border-radius: 8px !important; | |
} | |
.gradio-container .gr-radio-group label { | |
color: #2d2d2d !important; | |
background: transparent !important; | |
} | |
""" | |
# ===== 创建UI ===== | |
def create_interface(): | |
with gr.Blocks(css=css, title="Adult NSFW AI Image Generator") as interface: | |
with gr.Column(elem_classes=["main-content"]): | |
# 简化标题 | |
gr.HTML('<div class="title">Adult NSFW AI Image Generator</div>') | |
# 简化警告信息 | |
gr.HTML(''' | |
<div class="warning-box"> | |
⚠️ 18+ CONTENT WARNING ⚠️ | |
</div> | |
''') | |
# 主要输入区域 | |
with gr.Row(): | |
# 左侧:提示词输入 | |
with gr.Column(scale=2): | |
prompt_input = gr.Textbox( | |
label="Detailed Prompt", | |
placeholder="Enter your detailed prompt here...", | |
lines=15, | |
elem_classes=["prompt-box"] | |
) | |
negative_prompt_input = gr.Textbox( | |
label="Negative Prompt (Optional)", | |
placeholder="Things you don't want in the image...", | |
lines=4, | |
elem_classes=["prompt-box"] | |
) | |
# 右侧:控制选项 | |
with gr.Column(scale=1): | |
# Style选项 | |
with gr.Group(elem_classes=["controls-section"]): | |
style_input = gr.Radio( | |
label="Style Preset", | |
choices=list(STYLE_PRESETS.keys()), | |
value="Realistic" | |
) | |
# Seed选项 | |
with gr.Group(elem_classes=["controls-section"]): | |
seed_input = gr.Number( | |
label="Seed (-1 for random)", | |
value=-1, | |
precision=0 | |
) | |
# 宽度选择 | |
with gr.Group(elem_classes=["controls-section"]): | |
width_input = gr.Slider( | |
label="Width", | |
minimum=512, | |
maximum=2048, | |
value=1024, | |
step=64 | |
) | |
# 高度选择 | |
with gr.Group(elem_classes=["controls-section"]): | |
height_input = gr.Slider( | |
label="Height", | |
minimum=512, | |
maximum=2048, | |
value=1024, | |
step=64 | |
) | |
# 高级参数 | |
with gr.Group(elem_classes=["controls-section"]): | |
steps_input = gr.Slider( | |
label="Steps", | |
minimum=10, | |
maximum=50, | |
value=28, | |
step=1 | |
) | |
cfg_input = gr.Slider( | |
label="CFG Scale", | |
minimum=1.0, | |
maximum=15.0, | |
value=7.0, | |
step=0.1 | |
) | |
# 生成按钮 | |
generate_button = gr.Button( | |
"GENERATE", | |
elem_classes=["generate-btn"], | |
variant="primary" | |
) | |
# 图片输出区域 | |
image_output = gr.Image( | |
label="Generated Image", | |
elem_classes=["image-output"], | |
show_label=False, | |
container=True | |
) | |
# 图片信息显示 | |
with gr.Row(): | |
generation_info = gr.Textbox( | |
label="Generation Info", | |
interactive=False, | |
elem_classes=["image-info"], | |
show_label=True, | |
visible=False | |
) | |
# 元数据显示区域 - 移除下载功能,只显示可复制的文本 | |
with gr.Row(): | |
metadata_display = gr.Textbox( | |
label="Image Metadata (Copy to save)", | |
interactive=True, | |
elem_classes=["metadata-box"], | |
show_label=True, | |
lines=15, | |
visible=False, | |
placeholder="Generated image metadata will appear here..." | |
) | |
# 生成图片的主要函数 | |
def on_generate(prompt, style, neg_prompt, steps, cfg, seed, width, height): | |
image, info, metadata = generate_image( | |
prompt, style, neg_prompt, steps, cfg, seed, width, height | |
) | |
if image is not None: | |
return ( | |
image, # 图片输出 | |
info, # 生成信息 | |
metadata, # 元数据 | |
gr.update(visible=True, value=info), # 显示生成信息 | |
gr.update(visible=True, value=metadata) # 显示元数据 | |
) | |
else: | |
return ( | |
None, | |
info, | |
"", | |
gr.update(visible=False), | |
gr.update(visible=False) | |
) | |
# 绑定生成事件 | |
generate_button.click( | |
fn=on_generate, | |
inputs=[ | |
prompt_input, style_input, negative_prompt_input, | |
steps_input, cfg_input, seed_input, width_input, height_input | |
], | |
outputs=[ | |
image_output, generation_info, metadata_display, | |
generation_info, metadata_display | |
], | |
show_progress=True | |
) | |
# 支持Enter键触发 | |
prompt_input.submit( | |
fn=on_generate, | |
inputs=[ | |
prompt_input, style_input, negative_prompt_input, | |
steps_input, cfg_input, seed_input, width_input, height_input | |
], | |
outputs=[ | |
image_output, generation_info, metadata_display, | |
generation_info, metadata_display | |
], | |
show_progress=True | |
) | |
# 启动时显示欢迎信息 | |
interface.load( | |
fn=lambda: ( | |
None, "", "", | |
gr.update(visible=False), | |
gr.update(visible=False) | |
), | |
outputs=[ | |
image_output, generation_info, metadata_display, | |
generation_info, metadata_display | |
] | |
) | |
return interface | |
# ===== 启动应用 ===== | |
if __name__ == "__main__": | |
print("🎨 Starting Simplified NSFW Image Generator...") | |
print(f"🔧 Fixed Model: {FIXED_MODEL}") | |
print(f"🔧 Quality LoRA: {QUALITY_LORA['description']}") | |
print(f"🔧 Spaces GPU: {'✅ Available' if SPACES_AVAILABLE else '❌ Not Available'}") | |
print(f"🔧 Compel Library: {'✅ Available' if COMPEL_AVAILABLE else '❌ Not Available'}") | |
print(f"🔧 CUDA: {'✅ Available' if torch.cuda.is_available() else '❌ Not Available'}") | |
app = create_interface() | |
app.queue(max_size=10, default_concurrency_limit=2) | |
app.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
show_error=True, | |
share=False | |
) |