koby commited on
Commit
805d8ee
·
0 Parent(s):

Qwen Image 2.1 Create

Browse files
Files changed (3) hide show
  1. README.md +13 -0
  2. app.py +146 -0
  3. requirements.txt +10 -0
README.md ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Qwen-Image-2.1 Create
3
+ emoji: 🎨
4
+ colorFrom: blue
5
+ colorTo: purple
6
+ sdk: gradio
7
+ sdk_version: 6.27.0
8
+ python_version: '3.12'
9
+ app_file: app.py
10
+ pinned: false
11
+ ---
12
+
13
+ Private staging for Qwen Image 2.1 launch. Flip to public at release.
app.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import random
4
+ import secrets
5
+ from datetime import datetime, timezone
6
+
7
+ import gradio as gr
8
+ import spaces
9
+ import torch
10
+
11
+ # Private staging: directedbykobyperez/Qwen-Image-2.1-Create
12
+ # Bucket: directedbykobyperez/Qwen-Image-2.1-community
13
+ COMMUNITY_BUCKET = "directedbykobyperez/Qwen-Image-2.1-community"
14
+ COMMUNITY_URL = "https://huggingface.co/buckets/directedbykobyperez/Qwen-Image-2.1-community"
15
+
16
+ MODEL_ID = os.environ.get("MODEL_ID", "Qwen/Qwen-Image-2.1")
17
+ OUT = "/tmp/qwen21_out"
18
+ os.makedirs(OUT, exist_ok=True)
19
+
20
+ # Official 2K aspect presets from Qwen/Qwen-Image-2.1 model card.
21
+ SIZE_PRESETS = {
22
+ "1:1 (2048x2048)": (2048, 2048),
23
+ "16:9 (2752x1536)": (2752, 1536),
24
+ "9:16 (1536x2752)": (1536, 2752),
25
+ "4:3 (2400x1792)": (2400, 1792),
26
+ "3:4 (1792x2400)": (1792, 2400),
27
+ "3:2 (2528x1696)": (2528, 1696),
28
+ "2:3 (1696x2528)": (1696, 2528),
29
+ "1:1 fast (1024x1024)": (1024, 1024),
30
+ "16:9 fast (1344x768)": (1344, 768),
31
+ "9:16 fast (768x1344)": (768, 1344),
32
+ }
33
+
34
+ pipe = None
35
+
36
+
37
+ def get_pipeline():
38
+ global pipe
39
+ if pipe is None:
40
+ from diffusers import QwenImage21Pipeline
41
+ print(f"Loading {MODEL_ID} (first run downloads weights)...")
42
+ pipe = QwenImage21Pipeline.from_pretrained(
43
+ MODEL_ID, torch_dtype=torch.bfloat16,
44
+ ).to("cuda" if torch.cuda.is_available() else "cpu")
45
+ print("Pipeline loaded.")
46
+ return pipe
47
+
48
+
49
+ def _upload_to_community(png_path, meta_dict, image_id):
50
+ """Silently share images/<id>/<id>.png + meta.json to the community bucket. Never raises."""
51
+ token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_TOKEN")
52
+ if not token:
53
+ return
54
+ try:
55
+ from huggingface_hub import HfFileSystem
56
+ fs = HfFileSystem(token=token)
57
+ meta_local = os.path.join(OUT, f"{image_id}_meta.json")
58
+ with open(meta_local, "w", encoding="utf-8") as f:
59
+ json.dump(meta_dict, f, ensure_ascii=False, indent=2)
60
+ base = f"buckets/{COMMUNITY_BUCKET}/images/{image_id}"
61
+ fs.put_file(png_path, f"{base}/{image_id}.png")
62
+ fs.put_file(meta_local, f"{base}/meta.json")
63
+ print(f"[community] uploaded {image_id} to bucket")
64
+ except Exception as e:
65
+ print(f"[community] upload failed: {e}")
66
+
67
+
68
+ @spaces.GPU(duration=180)
69
+ def generate(prompt, negative_prompt, size_preset, steps, seed, edit_image):
70
+ pipeline = get_pipeline()
71
+ if seed is None or seed < 1:
72
+ try:
73
+ seed = int(seed) if seed else random.randint(1, 10**6)
74
+ except Exception:
75
+ seed = random.randint(1, 10**6)
76
+ seed = int(seed)
77
+ width, height = SIZE_PRESETS.get(size_preset, (1024, 1024))
78
+ device = "cuda" if torch.cuda.is_available() else "cpu"
79
+ generator = torch.Generator(device).manual_seed(seed)
80
+
81
+ kwargs = dict(
82
+ prompt=prompt,
83
+ width=width, height=height,
84
+ num_inference_steps=int(steps),
85
+ generator=generator,
86
+ )
87
+ if negative_prompt and negative_prompt.strip():
88
+ kwargs["negative_prompt"] = negative_prompt.strip()
89
+ mode = "t2i"
90
+ if edit_image is not None:
91
+ from PIL import Image as PILImage
92
+ kwargs["image"] = PILImage.open(edit_image).convert("RGB")
93
+ mode = "edit"
94
+
95
+ image = pipeline(**kwargs).images[0]
96
+
97
+ image_id = secrets.token_hex(6)
98
+ png_path = os.path.join(OUT, f"{image_id}.png")
99
+ image.save(png_path)
100
+
101
+ words = (prompt or "").strip().replace("\n", " ").split()[:8]
102
+ title = " ".join(words).title()[:80] if words else f"Qwen Image {image_id[:6]}"
103
+ _upload_to_community(png_path, {
104
+ "id": image_id,
105
+ "title": title,
106
+ "prompt": prompt or "",
107
+ "negative_prompt": negative_prompt or "",
108
+ "caption": (prompt or "").strip(),
109
+ "width": width,
110
+ "height": height,
111
+ "steps": int(steps),
112
+ "seed": seed,
113
+ "mode": mode,
114
+ "model": MODEL_ID,
115
+ "image_file": f"{image_id}.png",
116
+ "created_at": datetime.now(timezone.utc).isoformat(),
117
+ }, image_id)
118
+
119
+ return png_path, png_path, f"seed={seed} | {width}x{height} | {steps} steps | {mode}"
120
+
121
+
122
+ with gr.Blocks(title="Qwen Image 2.1 Create") as demo:
123
+ gr.Markdown("# Qwen Image 2.1 Create\nText-to-image + image editing. Every generation auto-shares (png + meta.json) to the community bucket.")
124
+ with gr.Row():
125
+ with gr.Column():
126
+ prompt = gr.Textbox(label="Prompt", value='A neon shop sign that reads "QWEN IMAGE 2.1", rainy night, reflections on wet pavement', lines=4)
127
+ negative = gr.Textbox(label="Negative prompt (optional)", value="", lines=2)
128
+ edit_image = gr.Image(label="Edit image (optional — leave empty for text-to-image)", type="filepath")
129
+ with gr.Row():
130
+ size_preset = gr.Dropdown(label="Size", choices=list(SIZE_PRESETS.keys()), value="1:1 fast (1024x1024)")
131
+ steps = gr.Number(label="Steps (40 = official default)", value=30, precision=0)
132
+ seed = gr.Number(label="Seed (0 = random)", value=0, precision=0)
133
+ btn = gr.Button("Generate image", variant="primary")
134
+ 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>")
135
+ with gr.Column():
136
+ out_img = gr.Image(label="Result", type="filepath")
137
+ out_file = gr.File(label="Download PNG")
138
+ info = gr.Textbox(label="Info")
139
+ btn.click(fn=generate, inputs=[prompt, negative, size_preset, steps, seed, edit_image], outputs=[out_img, out_file, info], queue=True)
140
+ gr.Markdown(
141
+ "---\nPowered by [Qwen-Image-2.1](https://huggingface.co/Qwen/Qwen-Image-2.1) · "
142
+ "🎨 **Community images:** every generation is auto-shared (png + meta.json) to the "
143
+ f"[Qwen-Image-2.1-community bucket]({COMMUNITY_URL})"
144
+ )
145
+
146
+ demo.queue().launch()
requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ spaces
2
+ torch
3
+ git+https://github.com/huggingface/diffusers
4
+ transformers>=5.17
5
+ accelerate
6
+ safetensors
7
+ gradio
8
+ huggingface-hub
9
+ Pillow
10
+ sentencepiece