Download app.py from c83908758/wode: direct link, hf CLI and curl.
- Browser
- Download file 7.11 kB
-
https://huggingface.co/spaces/c83908758/wode/resolve/main/app.py
- Command line
-
hf download hf://spaces/c83908758/wode/app.py
-
curl -L -o app.py https://huggingface.co/spaces/c83908758/wode/resolve/main/app.py
7.11 kB
| import gc | |
| import os | |
| from typing import Optional | |
| import gradio as gr | |
| import torch | |
| from diffusers import AutoPipelineForText2Image, EulerAncestralDiscreteScheduler | |
| MODEL_ID = os.getenv("MODEL_ID", "stablediffusionapi/counterfeit-v30") | |
| DEFAULT_NEGATIVE = os.getenv( | |
| "DEFAULT_NEGATIVE", | |
| "blurry, lowres, worst quality, low quality, bad anatomy, watermark, text, extra fingers, realistic, photo, 3d, child, loli, young-looking", | |
| ) | |
| PRESETS = { | |
| "🎀 动漫头像": { | |
| "prompt": "masterpiece, best quality, 1girl, solo, anime style, upper body portrait, detailed eyes, clean lineart, soft cel shading", | |
| "negative": DEFAULT_NEGATIVE, | |
| "steps": 12, | |
| "cfg": 7.0, | |
| "width": 384, | |
| "height": 576, | |
| "seed": -1, | |
| }, | |
| "💼 成熟御姐": { | |
| "prompt": "masterpiece, best quality, 1woman, mature female, solo, anime style, office lady, detailed eyes, upper body portrait, clean lineart", | |
| "negative": DEFAULT_NEGATIVE, | |
| "steps": 12, | |
| "cfg": 7.0, | |
| "width": 384, | |
| "height": 576, | |
| "seed": -1, | |
| }, | |
| "🌸 樱花和服": { | |
| "prompt": "masterpiece, best quality, 1girl, anime style, kimono, cherry blossoms, elegant pose, detailed face, clean lineart", | |
| "negative": DEFAULT_NEGATIVE, | |
| "steps": 14, | |
| "cfg": 7.0, | |
| "width": 448, | |
| "height": 640, | |
| "seed": -1, | |
| }, | |
| "🏙 赛博少女": { | |
| "prompt": "masterpiece, best quality, 1girl, anime style, cyberpunk city, neon lights, upper body, detailed eyes, clean lineart", | |
| "negative": DEFAULT_NEGATIVE, | |
| "steps": 14, | |
| "cfg": 7.0, | |
| "width": 448, | |
| "height": 640, | |
| "seed": -1, | |
| }, | |
| } | |
| _pipe = None | |
| _device = "cuda" if torch.cuda.is_available() else "cpu" | |
| _dtype = torch.float16 if _device == "cuda" else torch.float32 | |
| def load_pipe(): | |
| global _pipe | |
| if _pipe is None: | |
| pipe = AutoPipelineForText2Image.from_pretrained( | |
| MODEL_ID, | |
| torch_dtype=_dtype, | |
| safety_checker=None, | |
| ) | |
| if hasattr(pipe, "safety_checker"): | |
| pipe.safety_checker = None | |
| if hasattr(pipe, "requires_safety_checker"): | |
| pipe.requires_safety_checker = False | |
| if hasattr(pipe, "scheduler") and pipe.scheduler is not None: | |
| pipe.scheduler = EulerAncestralDiscreteScheduler.from_config(pipe.scheduler.config) | |
| if hasattr(pipe, "enable_attention_slicing"): | |
| pipe.enable_attention_slicing() | |
| if hasattr(pipe, "enable_vae_slicing"): | |
| pipe.enable_vae_slicing() | |
| if hasattr(pipe, "vae") and hasattr(pipe.vae, "enable_tiling"): | |
| pipe.vae.enable_tiling() | |
| pipe = pipe.to(_device) | |
| _pipe = pipe | |
| return _pipe | |
| def unload_pipe(): | |
| global _pipe | |
| if _pipe is not None: | |
| _pipe = None | |
| gc.collect() | |
| if torch.cuda.is_available(): | |
| torch.cuda.empty_cache() | |
| def apply_preset(name: str): | |
| p = PRESETS[name] | |
| return ( | |
| p["prompt"], | |
| p["negative"], | |
| p["steps"], | |
| p["cfg"], | |
| p["width"], | |
| p["height"], | |
| p["seed"], | |
| ) | |
| def generate( | |
| prompt: str, | |
| negative_prompt: str, | |
| steps: int, | |
| guidance_scale: float, | |
| width: int, | |
| height: int, | |
| seed: int, | |
| ): | |
| if not prompt or not prompt.strip(): | |
| raise gr.Error("提示词不能为空") | |
| pipe = load_pipe() | |
| generator: Optional[torch.Generator] = None | |
| if seed >= 0: | |
| generator = torch.Generator(device=_device).manual_seed(int(seed)) | |
| with torch.inference_mode(): | |
| image = pipe( | |
| prompt=prompt, | |
| negative_prompt=negative_prompt, | |
| num_inference_steps=int(steps), | |
| guidance_scale=float(guidance_scale), | |
| width=int(width), | |
| height=int(height), | |
| generator=generator, | |
| ).images[0] | |
| return image | |
| with gr.Blocks(title="wode") as demo: | |
| gr.Markdown( | |
| f"# wode\n\n免费档二次元生图(Counterfeit 路线)\n\n当前模型:`{MODEL_ID}`\n\n推荐先用:`384x576 / 12 steps / CFG 7`。\n\n想更像动漫角色:正向词尽量写 `1girl/1woman + anime style + detailed eyes + clean lineart`。\n想偏成年风格:加 `mature female` 或 `adult woman`。默认反向词已压制 `realistic/photo` 和 `child-like`。\n\n⚠️ 免费 CPU 仍然会慢,通常要等 `1~5 分钟`。" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| prompt = gr.Textbox( | |
| label="正向提示词", | |
| lines=4, | |
| placeholder="例如:masterpiece, best quality, 1girl, anime style, detailed eyes, clean lineart", | |
| ) | |
| negative_prompt = gr.Textbox(label="反向提示词", lines=3, value=DEFAULT_NEGATIVE) | |
| with gr.Row(): | |
| steps = gr.Slider(6, 24, value=12, step=1, label="步数 Steps") | |
| guidance_scale = gr.Slider(1, 12, value=7.0, step=0.5, label="引导强度 CFG") | |
| with gr.Row(): | |
| width = gr.Slider(256, 640, value=384, step=64, label="宽度") | |
| height = gr.Slider(256, 896, value=576, step=64, label="高度") | |
| seed = gr.Number(label="随机种子(-1 为随机)", value=-1, precision=0) | |
| with gr.Row(): | |
| run = gr.Button("开始生成", variant="primary") | |
| unload = gr.Button("释放模型内存") | |
| with gr.Column(): | |
| out = gr.Image(label="生成结果", type="pil") | |
| gr.Markdown("## 二次元预设按钮") | |
| with gr.Row(): | |
| preset1 = gr.Button("🎀 动漫头像") | |
| preset2 = gr.Button("💼 成熟御姐") | |
| preset3 = gr.Button("🌸 樱花和服") | |
| preset4 = gr.Button("🏙 赛博少女") | |
| for btn, name in [ | |
| (preset1, "🎀 动漫头像"), | |
| (preset2, "💼 成熟御姐"), | |
| (preset3, "🌸 樱花和服"), | |
| (preset4, "🏙 赛博少女"), | |
| ]: | |
| btn.click( | |
| fn=lambda n=name: apply_preset(n), | |
| inputs=[], | |
| outputs=[prompt, negative_prompt, steps, guidance_scale, width, height, seed], | |
| ) | |
| gr.Examples( | |
| label="也可以直接点下面示例", | |
| examples=[ | |
| [ | |
| PRESETS["🎀 动漫头像"]["prompt"], | |
| DEFAULT_NEGATIVE, | |
| 12, | |
| 7.0, | |
| 384, | |
| 576, | |
| -1, | |
| ], | |
| [ | |
| PRESETS["💼 成熟御姐"]["prompt"], | |
| DEFAULT_NEGATIVE, | |
| 12, | |
| 7.0, | |
| 384, | |
| 576, | |
| -1, | |
| ], | |
| ], | |
| inputs=[prompt, negative_prompt, steps, guidance_scale, width, height, seed], | |
| ) | |
| run.click( | |
| fn=generate, | |
| inputs=[prompt, negative_prompt, steps, guidance_scale, width, height, seed], | |
| outputs=out, | |
| ) | |
| unload.click(fn=unload_pipe, outputs=[]) | |
| if __name__ == "__main__": | |
| demo.queue(max_size=4).launch(show_error=True) | |