Spaces:
Running on Zero
Running on Zero
File size: 7,016 Bytes
805d8ee a427ccd 805d8ee 49ae619 a427ccd 805d8ee a427ccd 805d8ee | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | import json
import os
import random
import secrets
from datetime import datetime, timezone
import gradio as gr
import spaces
import torch
# Private staging: directedbykobyperez/Qwen-Image-2.1-Create
# Bucket: directedbykobyperez/Qwen-Image-2.1-community
COMMUNITY_BUCKET = "directedbykobyperez/Qwen-Image-2.1-community"
COMMUNITY_URL = "https://huggingface.co/buckets/directedbykobyperez/Qwen-Image-2.1-community"
MODEL_ID = os.environ.get("MODEL_ID", "Qwen/Qwen-Image-2.1")
OUT = "/tmp/qwen21_out"
os.makedirs(OUT, exist_ok=True)
# Official 2K aspect presets from Qwen/Qwen-Image-2.1 model card.
SIZE_PRESETS = {
"1:1 (2048x2048)": (2048, 2048),
"16:9 (2752x1536)": (2752, 1536),
"9:16 (1536x2752)": (1536, 2752),
"4:3 (2400x1792)": (2400, 1792),
"3:4 (1792x2400)": (1792, 2400),
"3:2 (2528x1696)": (2528, 1696),
"2:3 (1696x2528)": (1696, 2528),
"1:1 fast (1024x1024)": (1024, 1024),
"16:9 fast (1344x768)": (1344, 768),
"9:16 fast (768x1344)": (768, 1344),
}
pipe = None
# Flagged words: generation proceeds normally, but flagged results are NOT
# uploaded to the public community bucket. Silent, no user-facing warning.
FLAGGED = (
"naked", "nude", "nsfw", "porn", "pornographic", "hentai", "erotic",
"sex", "sexual", "undress", "unclothed", "topless", "bottomless",
"nak3d", "nudes", "bathing", "panties", "lingerie", "underwear",
"orgasm", "nipple", "nipples", "genital", "penis", "vagina", "boobs",
"titties", "incest", "rape",
"child", "kid ", "kids", "minor", "teen", "teenager", "underage",
"schoolgirl", "schoolboy", "loli", "shota", "toddler", "infant",
"小女孩", "小男孩", "少女", "儿童", "裸体", "裸足", "脱下", "脱掉",
"裸", "色情", "性爱", "幼女",
)
def is_flagged(*texts):
joined = " " + " ".join(t or "" for t in texts).lower() + " "
return any(w in joined for w in FLAGGED)
def get_pipeline():
global pipe
if pipe is None:
from diffusers import QwenImage21Pipeline
print(f"Loading {MODEL_ID} (first run downloads weights)...")
pipe = QwenImage21Pipeline.from_pretrained(
MODEL_ID, torch_dtype=torch.bfloat16,
).to("cuda" if torch.cuda.is_available() else "cpu")
print("Pipeline loaded.")
return pipe
def _upload_to_community(png_path, meta_dict, image_id):
"""Silently share images/<id>/<id>.png + meta.json to the community bucket. Never raises."""
token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_TOKEN")
if not token:
return
try:
from huggingface_hub import HfFileSystem
fs = HfFileSystem(token=token)
meta_local = os.path.join(OUT, f"{image_id}_meta.json")
with open(meta_local, "w", encoding="utf-8") as f:
json.dump(meta_dict, f, ensure_ascii=False, indent=2)
base = f"buckets/{COMMUNITY_BUCKET}/images/{image_id}"
fs.put_file(png_path, f"{base}/{image_id}.png")
fs.put_file(meta_local, f"{base}/meta.json")
print(f"[community] uploaded {image_id} to bucket")
except Exception as e:
print(f"[community] upload failed: {e}")
@spaces.GPU(duration=180)
def generate(prompt, negative_prompt, size_preset, steps, seed, edit_image):
pipeline = get_pipeline()
if seed is None or seed < 1:
try:
seed = int(seed) if seed else random.randint(1, 10**6)
except Exception:
seed = random.randint(1, 10**6)
seed = int(seed)
width, height = SIZE_PRESETS.get(size_preset, (1024, 1024))
device = "cuda" if torch.cuda.is_available() else "cpu"
generator = torch.Generator(device).manual_seed(seed)
kwargs = dict(
prompt=prompt,
width=width, height=height,
num_inference_steps=int(steps),
generator=generator,
)
if negative_prompt and negative_prompt.strip():
kwargs["negative_prompt"] = negative_prompt.strip()
mode = "t2i"
if edit_image is not None:
from PIL import Image as PILImage
kwargs["image"] = PILImage.open(edit_image).convert("RGB")
mode = "edit"
image = pipeline(**kwargs).images[0]
image_id = secrets.token_hex(6)
png_path = os.path.join(OUT, f"{image_id}.png")
image.save(png_path)
flagged = is_flagged(prompt, negative_prompt) or mode == "edit"
words = (prompt or "").strip().replace("\n", " ").split()[:8]
title = " ".join(words).title()[:80] if words else f"Qwen Image {image_id[:6]}"
if not flagged:
_upload_to_community(png_path, {
"id": image_id,
"title": title,
"prompt": prompt or "",
"negative_prompt": negative_prompt or "",
"caption": (prompt or "").strip(),
"width": width,
"height": height,
"steps": int(steps),
"seed": seed,
"mode": mode,
"model": MODEL_ID,
"image_file": f"{image_id}.png",
"created_at": datetime.now(timezone.utc).isoformat(),
}, image_id)
return png_path, png_path, f"seed={seed} | {width}x{height} | {steps} steps | {mode}"
with gr.Blocks(title="Qwen Image 2.1 Create") as demo:
gr.Markdown("# Qwen Image 2.1 Create\nText-to-image + image editing. Every generation auto-shares (png + meta.json) to the community bucket.")
with gr.Row():
with gr.Column():
prompt = gr.Textbox(label="Prompt", value='A neon shop sign that reads "QWEN IMAGE 2.1", rainy night, reflections on wet pavement', lines=4)
negative = gr.Textbox(label="Negative prompt (optional)", value="", lines=2)
edit_image = gr.Image(label="Edit image (optional — leave empty for text-to-image)", type="filepath")
with gr.Row():
size_preset = gr.Dropdown(label="Size", choices=list(SIZE_PRESETS.keys()), value="1:1 fast (1024x1024)")
steps = gr.Number(label="Steps (40 = official default)", value=30, precision=0)
seed = gr.Number(label="Seed (0 = random)", value=0, precision=0)
btn = gr.Button("Generate image", variant="primary")
gr.HTML("<div style='text-align:center;margin:-12px 0 -8px;font-size:0.9rem;'>Don't forget to<a style='margin-left:5px;padding:0;' href='https://huggingface.co/Qwen/Qwen-Image-2.1' target='_blank'>like the model ❤️</a></div>")
with gr.Column():
out_img = gr.Image(label="Result", type="filepath")
out_file = gr.File(label="Download PNG")
info = gr.Textbox(label="Info")
btn.click(fn=generate, inputs=[prompt, negative, size_preset, steps, seed, edit_image], outputs=[out_img, out_file, info], queue=True)
gr.Markdown(
"---\nPowered by [Qwen-Image-2.1](https://huggingface.co/Qwen/Qwen-Image-2.1) · "
"🎨 **Community images:** every generation is auto-shared (png + meta.json) to the "
f"[Qwen-Image-2.1-community bucket]({COMMUNITY_URL})"
)
demo.queue().launch()
|