directedbykobyperez's picture
Upload app.py with huggingface_hub
49ae619 verified
Raw
History Blame Contribute Delete
7.02 kB
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()