# ===== 必须首先导入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 @apply_spaces_decorator 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('