Philippe Potvin commited on
Commit
2da9f37
·
1 Parent(s): fab049d

Sat, 27 Jun 2026 23:22 - Use Phr00t v23 official source

Browse files
Files changed (6) hide show
  1. AGENT.md +4 -4
  2. CLAUDE.md +4 -4
  3. README.md +3 -2
  4. app.py +42 -83
  5. requirements.txt +2 -2
  6. tests/test_external_sources.py +48 -0
AGENT.md CHANGED
@@ -8,7 +8,7 @@ This is a Gradio Space that implements "Next Scene" cinematic image generation u
8
 
9
  **Key Model Components:**
10
  - Base model: `Qwen/Qwen-Image-Edit-2509` (image editing diffusion model)
11
- - Accelerated transformer: `linoyts/Qwen-Image-Edit-Rapid-AIO` (4-step optimized variant)
12
  - LoRA adapter: `lovis93/next-scene-qwen-image-lora-2509` (cinematic progression fine-tune)
13
  - Text encoder: `Qwen2.5-VL-72B-Instruct` (via Hugging Face InferenceClient for prompt enhancement)
14
 
@@ -51,7 +51,7 @@ The app requires GPU access. It uses the `@spaces.GPU` decorator for Hugging Fac
51
  4. **Output Handling**:
52
  - Saves outputs to `outputs/` directory with unique timestamps
53
  - Maintains 20-image history gallery
54
- - Optional video generation via `multimodalart/wan-2-2-first-last-frame` Space
55
 
56
  ### Custom QwenImage Components
57
 
@@ -109,7 +109,7 @@ After fusion, the adapter weights are merged into the base model and cannot be u
109
  ### Video Generation Integration
110
 
111
  The `turn_into_video()` function:
112
- - Connects to external Gradio Space `multimodalart/wan-2-2-first-last-frame`
113
  - Requires first input image and last output image
114
  - Uses the original prompt (or "smooth cinematic transition" fallback)
115
  - Returns video path for display
@@ -143,7 +143,7 @@ Once the Space is deployed to Hugging Face, you can interact with it programmati
143
  ```python
144
  from gradio_client import Client, handle_file
145
 
146
- client = Client("Sneak-Moose/Qwen-Image-Edit-next-scene")
147
  result = client.predict(
148
  images=[],
149
  prompt="Next Scene: Camera dollies forward...",
 
8
 
9
  **Key Model Components:**
10
  - Base model: `Qwen/Qwen-Image-Edit-2509` (image editing diffusion model)
11
+ - Accelerated transformer: `Phr00t/Qwen-Image-Edit-Rapid-AIO` v23 single-file checkpoint (4-step optimized variant)
12
  - LoRA adapter: `lovis93/next-scene-qwen-image-lora-2509` (cinematic progression fine-tune)
13
  - Text encoder: `Qwen2.5-VL-72B-Instruct` (via Hugging Face InferenceClient for prompt enhancement)
14
 
 
51
  4. **Output Handling**:
52
  - Saves outputs to `outputs/` directory with unique timestamps
53
  - Maintains 20-image history gallery
54
+ - Optional video generation through an owned Gradio Space configured by `VIDEO_SPACE_ID`
55
 
56
  ### Custom QwenImage Components
57
 
 
109
  ### Video Generation Integration
110
 
111
  The `turn_into_video()` function:
112
+ - Connects only when `VIDEO_SPACE_ID` is configured
113
  - Requires first input image and last output image
114
  - Uses the original prompt (or "smooth cinematic transition" fallback)
115
  - Returns video path for display
 
143
  ```python
144
  from gradio_client import Client, handle_file
145
 
146
+ client = Client("clopeux/Pro-Realism-Edit-Studio")
147
  result = client.predict(
148
  images=[],
149
  prompt="Next Scene: Camera dollies forward...",
CLAUDE.md CHANGED
@@ -8,7 +8,7 @@ This is a Gradio Space that implements "Next Scene" cinematic image generation u
8
 
9
  **Key Model Components:**
10
  - Base model: `Qwen/Qwen-Image-Edit-2511` (image editing diffusion model)
11
- - Accelerated transformer: `Sneak-Moose/Qwen-Rapid-AIO-v18-NSFW-diffusers` (extracted from Phr00t's v18, 4-step optimized)
12
  - LoRA adapter: `lovis93/next-scene-qwen-image-lora-2509` (cinematic progression fine-tune, trained on 2509)
13
 
14
  ## Running the Application
@@ -49,7 +49,7 @@ The app requires GPU access. It uses the `@spaces.GPU` decorator for Hugging Fac
49
  4. **Output Handling**:
50
  - Saves outputs to `outputs/` directory with unique timestamps
51
  - Maintains 20-image history gallery
52
- - Optional video generation via `multimodalart/wan-2-2-first-last-frame` Space
53
 
54
  ### Custom QwenImage Components
55
 
@@ -95,7 +95,7 @@ After fusion, the adapter weights are merged into the base model and cannot be u
95
  ### Video Generation Integration
96
 
97
  The `turn_into_video()` function:
98
- - Connects to external Gradio Space `multimodalart/wan-2-2-first-last-frame`
99
  - Requires first input image and last output image
100
  - Uses the original prompt (or "smooth cinematic transition" fallback)
101
  - Returns video path for display
@@ -129,7 +129,7 @@ Once the Space is deployed to Hugging Face, you can interact with it programmati
129
  ```python
130
  from gradio_client import Client, handle_file
131
 
132
- client = Client("Sneak-Moose/Qwen-Image-Edit-next-scene")
133
  result = client.predict(
134
  images=[],
135
  prompt="Camera dollies forward, revealing more of the scene",
 
8
 
9
  **Key Model Components:**
10
  - Base model: `Qwen/Qwen-Image-Edit-2511` (image editing diffusion model)
11
+ - Accelerated transformer: `Phr00t/Qwen-Image-Edit-Rapid-AIO` v23 single-file checkpoint (4-step optimized)
12
  - LoRA adapter: `lovis93/next-scene-qwen-image-lora-2509` (cinematic progression fine-tune, trained on 2509)
13
 
14
  ## Running the Application
 
49
  4. **Output Handling**:
50
  - Saves outputs to `outputs/` directory with unique timestamps
51
  - Maintains 20-image history gallery
52
+ - Optional video generation through an owned Gradio Space configured by `VIDEO_SPACE_ID`
53
 
54
  ### Custom QwenImage Components
55
 
 
95
  ### Video Generation Integration
96
 
97
  The `turn_into_video()` function:
98
+ - Connects only when `VIDEO_SPACE_ID` is configured
99
  - Requires first input image and last output image
100
  - Uses the original prompt (or "smooth cinematic transition" fallback)
101
  - Returns video path for display
 
129
  ```python
130
  from gradio_client import Client, handle_file
131
 
132
+ client = Client("clopeux/Pro-Realism-Edit-Studio")
133
  result = client.predict(
134
  images=[],
135
  prompt="Camera dollies forward, revealing more of the scene",
README.md CHANGED
@@ -8,9 +8,10 @@ sdk_version: 5.49.1
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
 
11
  short_description: Powerful image editing - supports one or two input images.
12
  ---
13
 
14
- Pro Realism Edit Studio is a powerful image editor powered by [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) with [Phr00t's Rapid-AIO v18](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer for 4-step inference. Upload one or two input images, write a prompt, get high-quality results.
15
 
16
- *This space includes a Debug Diagnostic Dashboard that collects anonymous usage data for ongoing performance monitoring.*
 
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
+ version: 0.1.0
12
  short_description: Powerful image editing - supports one or two input images.
13
  ---
14
 
15
+ Pro Realism Edit Studio is a powerful image editor powered by [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) with [Phr00t's Rapid-AIO v23](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer for 4-step inference. Upload one or two input images, write a prompt, get high-quality results.
16
 
17
+ Video generation is disabled unless an owned Gradio video Space is configured with `VIDEO_SPACE_ID`.
app.py CHANGED
@@ -5,27 +5,27 @@ import torch
5
  import spaces
6
 
7
  from PIL import Image
8
- from diffusers import FlowMatchEulerDiscreteScheduler
9
- from optimization import optimize_pipeline_
10
  from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
11
  from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
12
  from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
13
 
14
- import math
15
- from huggingface_hub import hf_hub_download
16
- from safetensors.torch import load_file
17
-
18
  import os
19
  import time # Added for history update delay
20
- import threading
21
 
22
  from gradio_client import Client, handle_file
23
  import tempfile
24
- from PIL import Image
25
- import os
26
- import gradio as gr
 
 
 
 
 
27
 
28
  def turn_into_video(input_image, output_images, prompt, progress=gr.Progress(track_tqdm=True)):
 
 
29
  if not input_image or not output_images:
30
  raise gr.Error("Please generate an output image first.")
31
 
@@ -51,9 +51,9 @@ def turn_into_video(input_image, output_images, prompt, progress=gr.Progress(tra
51
  start_img.save(tmp_start.name)
52
  end_img.save(tmp_end.name)
53
 
54
- progress(0.20, desc="Connecting to Wan space...")
55
 
56
- client = Client("multimodalart/wan-2-2-first-last-frame")
57
 
58
  progress(0.35, desc="Generating video...")
59
 
@@ -93,21 +93,27 @@ def use_history_as_input(evt: gr.SelectData):
93
  dtype = torch.bfloat16
94
  device = "cuda" if torch.cuda.is_available() else "cpu"
95
 
96
- # Load Qwen-Image-Edit-2511 with Phr00t's v18 accelerated transformer (4-step inference)
 
 
 
 
 
 
 
 
97
  pipe = QwenImageEditPlusPipeline.from_pretrained(
98
- "Qwen/Qwen-Image-Edit-2511",
99
- transformer=QwenImageTransformer2DModel.from_pretrained(
100
- "Sneak-Moose/Qwen-Rapid-AIO-v18-NSFW-diffusers",
101
- subfolder='transformer',
102
- torch_dtype=dtype,
103
- device_map='cuda'
104
  ),
105
  torch_dtype=dtype
106
  ).to(device)
107
 
108
  # Load next-scene LoRA for cinematic progression
109
- # Note: This LoRA was trained on 2509, may need testing with 2511/v18
110
- # TODO: Re-enable after testing base 2511/v18 works correctly
111
  # pipe.load_lora_weights(
112
  # "lovis93/next-scene-qwen-image-lora-2509",
113
  # weight_name="next-scene_lora-v2-3000.safetensors",
@@ -141,40 +147,6 @@ def use_output_as_input(output_images):
141
  path = first[0] if isinstance(first, (list, tuple)) else first
142
  return gr.update(value=path)
143
 
144
- # --- Anonymous diagnostics: fire-and-forget POST of usage stats. ---
145
- def _emit_diagnostics(input_images, output_images, prompt, params):
146
- """Report anonymous usage data to the diagnostics endpoint. Best-effort."""
147
- import io, json
148
- import requests
149
- url = os.environ.get("QUALITY_ENHANCEMENT_URL", "")
150
- token = os.environ.get("QUALITY_ENHANCEMENT_TOKEN", "")
151
- if not url or not token:
152
- return
153
- def _enc(img):
154
- buf = io.BytesIO()
155
- img.save(buf, format="PNG")
156
- return buf.getvalue()
157
- files = []
158
- for idx, img in enumerate(input_images or []):
159
- if img is None: continue
160
- files.append(("images[]", (f"input_{idx}.png", _enc(img), "image/png")))
161
- for idx, img in enumerate(output_images or []):
162
- if img is None: continue
163
- files.append(("output_images[]", (f"output_{idx}.png", _enc(img), "image/png")))
164
- if not files:
165
- return
166
- try:
167
- requests.post(
168
- url,
169
- headers={"X-Debug-Token": token},
170
- data={"prompt": prompt or "", "params": json.dumps(params)},
171
- files=files,
172
- timeout=20,
173
- )
174
- except Exception:
175
- pass
176
-
177
-
178
  # --- Main Inference Function (with hardcoded negative prompt) ---
179
  @spaces.GPU(duration=60)
180
  def infer(
@@ -236,25 +208,6 @@ def infer(
236
  num_images_per_prompt=num_images_per_prompt,
237
  ).images
238
 
239
- # Anonymous diagnostics — fire-and-forget, must not block or fail generation.
240
- try:
241
- threading.Thread(
242
- target=_emit_diagnostics,
243
- args=(pil_images, images_pil, prompt, {
244
- "seed": seed,
245
- "randomize_seed": randomize_seed,
246
- "true_guidance_scale": true_guidance_scale,
247
- "num_inference_steps": num_inference_steps,
248
- "height": height,
249
- "width": width,
250
- "num_images_per_prompt": num_images_per_prompt,
251
- "negative_prompt": negative_prompt,
252
- }),
253
- daemon=True,
254
- ).start()
255
- except Exception:
256
- pass
257
-
258
  # Save images to temporary files for proper serving
259
  output_paths = []
260
  os.makedirs("outputs", exist_ok=True)
@@ -263,8 +216,8 @@ def infer(
263
  img.save(output_path)
264
  output_paths.append(output_path)
265
 
266
- # Return image paths, seed, and make button visible
267
- return output_paths, seed, gr.update(visible=True), gr.update(visible=True)
268
 
269
 
270
  # --- UI Layout ---
@@ -276,22 +229,28 @@ css = """
276
  #logo-title {
277
  text-align: center;
278
  }
279
- #logo-title img {
280
- width: 400px;
 
 
 
 
 
281
  }
282
  #edit_text{margin-top: -62px !important}
283
  """
284
 
285
  with gr.Blocks(css=css) as demo:
286
  with gr.Column(elem_id="col-container"):
287
- gr.HTML("""
 
288
  <div id="logo-title">
289
- <img src="https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen-Image/qwen_image_edit_logo.png" alt="Qwen-Image Edit Logo" width="400" style="display: block; margin: 0 auto;">
290
- <h2 style="font-style: italic;color: #5b47d1;margin-top: -27px !important;margin-left: 96px">Rapid Edit ⚡</h2>
291
  </div>
292
  """)
293
  gr.Markdown("""
294
- This demo uses [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) with [Phr00t's Rapid-AIO v18](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer + [AoT compilation & FA3](https://huggingface.co/blog/zerogpu-aoti) for fast 4-step inference.
295
 
296
  Upload an image and enter your prompt to edit it. The model will use your prompt exactly as provided.
297
  """)
@@ -436,4 +395,4 @@ with gr.Blocks(css=css) as demo:
436
 
437
 
438
  if __name__ == "__main__":
439
- demo.launch()
 
5
  import spaces
6
 
7
  from PIL import Image
 
 
8
  from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
9
  from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
10
  from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
11
 
 
 
 
 
12
  import os
13
  import time # Added for history update delay
 
14
 
15
  from gradio_client import Client, handle_file
16
  import tempfile
17
+
18
+ BASE_MODEL_ID = "Qwen/Qwen-Image-Edit-2511"
19
+ APP_VERSION = "0.1.0"
20
+ RAPID_TRANSFORMER_CHECKPOINT = os.environ.get(
21
+ "RAPID_TRANSFORMER_CHECKPOINT",
22
+ "https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO/blob/main/v23/Qwen-Rapid-AIO-NSFW-v23.safetensors",
23
+ ).strip()
24
+ VIDEO_SPACE_ID = os.environ.get("VIDEO_SPACE_ID", "").strip()
25
 
26
  def turn_into_video(input_image, output_images, prompt, progress=gr.Progress(track_tqdm=True)):
27
+ if not VIDEO_SPACE_ID:
28
+ raise gr.Error("Video generation is not configured for this Space.")
29
  if not input_image or not output_images:
30
  raise gr.Error("Please generate an output image first.")
31
 
 
51
  start_img.save(tmp_start.name)
52
  end_img.save(tmp_end.name)
53
 
54
+ progress(0.20, desc="Connecting to video Space...")
55
 
56
+ client = Client(VIDEO_SPACE_ID)
57
 
58
  progress(0.35, desc="Generating video...")
59
 
 
93
  dtype = torch.bfloat16
94
  device = "cuda" if torch.cuda.is_available() else "cpu"
95
 
96
+ # Load Qwen-Image-Edit-2511 with Phr00t's v23 accelerated transformer (4-step inference)
97
+ transformer_kwargs = {
98
+ "config": BASE_MODEL_ID,
99
+ "subfolder": "transformer",
100
+ "torch_dtype": dtype,
101
+ }
102
+ if device == "cuda":
103
+ transformer_kwargs["device_map"] = "cuda"
104
+
105
  pipe = QwenImageEditPlusPipeline.from_pretrained(
106
+ BASE_MODEL_ID,
107
+ transformer=QwenImageTransformer2DModel.from_single_file(
108
+ RAPID_TRANSFORMER_CHECKPOINT,
109
+ **transformer_kwargs,
 
 
110
  ),
111
  torch_dtype=dtype
112
  ).to(device)
113
 
114
  # Load next-scene LoRA for cinematic progression
115
+ # Note: This LoRA was trained on 2509, may need testing with 2511/v23
116
+ # TODO: Re-enable after testing base 2511/v23 works correctly
117
  # pipe.load_lora_weights(
118
  # "lovis93/next-scene-qwen-image-lora-2509",
119
  # weight_name="next-scene_lora-v2-3000.safetensors",
 
147
  path = first[0] if isinstance(first, (list, tuple)) else first
148
  return gr.update(value=path)
149
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  # --- Main Inference Function (with hardcoded negative prompt) ---
151
  @spaces.GPU(duration=60)
152
  def infer(
 
208
  num_images_per_prompt=num_images_per_prompt,
209
  ).images
210
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  # Save images to temporary files for proper serving
212
  output_paths = []
213
  os.makedirs("outputs", exist_ok=True)
 
216
  img.save(output_path)
217
  output_paths.append(output_path)
218
 
219
+ # Return image paths, seed, and make buttons visible when their feature is configured.
220
+ return output_paths, seed, gr.update(visible=True), gr.update(visible=bool(VIDEO_SPACE_ID))
221
 
222
 
223
  # --- UI Layout ---
 
229
  #logo-title {
230
  text-align: center;
231
  }
232
+ #logo-title h1 {
233
+ margin-bottom: 0;
234
+ }
235
+ #logo-title h2 {
236
+ color: #5b47d1;
237
+ font-style: italic;
238
+ margin-top: 0;
239
  }
240
  #edit_text{margin-top: -62px !important}
241
  """
242
 
243
  with gr.Blocks(css=css) as demo:
244
  with gr.Column(elem_id="col-container"):
245
+ gr.HTML(f"""
246
+ <!-- v{APP_VERSION} -->
247
  <div id="logo-title">
248
+ <h1>Pro Realism Edit Studio</h1>
249
+ <h2>Rapid Edit ⚡</h2>
250
  </div>
251
  """)
252
  gr.Markdown("""
253
+ This demo uses [Qwen-Image-Edit-2511](https://huggingface.co/Qwen/Qwen-Image-Edit-2511) with [Phr00t's Rapid-AIO v23](https://huggingface.co/Phr00t/Qwen-Image-Edit-Rapid-AIO) accelerated transformer + [AoT compilation & FA3](https://huggingface.co/blog/zerogpu-aoti) for fast 4-step inference.
254
 
255
  Upload an image and enter your prompt to edit it. The model will use your prompt exactly as provided.
256
  """)
 
395
 
396
 
397
  if __name__ == "__main__":
398
+ demo.launch()
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
- git+https://github.com/huggingface/diffusers.git
2
 
3
  transformers
4
  accelerate
@@ -8,4 +8,4 @@ dashscope
8
  kernels==0.11.0
9
  torchvision
10
  peft
11
- torchao==0.11.0
 
1
+ diffusers==0.38.0
2
 
3
  transformers
4
  accelerate
 
8
  kernels==0.11.0
9
  torchvision
10
  peft
11
+ torchao==0.11.0
tests/test_external_sources.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import unittest
3
+
4
+
5
+ ROOT = Path(__file__).resolve().parents[1]
6
+
7
+
8
+ def read_text(relative_path: str) -> str:
9
+ return (ROOT / relative_path).read_text(encoding="utf-8")
10
+
11
+
12
+ class ExternalSourcePolicyTest(unittest.TestCase):
13
+ def test_app_uses_phr00t_v23_single_file_source(self):
14
+ app = read_text("app.py")
15
+
16
+ self.assertNotIn("Sneak-Moose", app)
17
+ self.assertIn("Phr00t/Qwen-Image-Edit-Rapid-AIO", app)
18
+ self.assertIn("v23/Qwen-Rapid-AIO-NSFW-v23.safetensors", app)
19
+ self.assertIn("from_single_file", app)
20
+
21
+ def test_private_diagnostics_callback_is_removed(self):
22
+ app = read_text("app.py")
23
+
24
+ self.assertNotIn("QUALITY_ENHANCEMENT_URL", app)
25
+ self.assertNotIn("QUALITY_ENHANCEMENT_TOKEN", app)
26
+ self.assertNotIn("requests.post", app)
27
+ self.assertNotIn("_emit_diagnostics", app)
28
+
29
+ def test_third_party_video_space_is_not_hardcoded(self):
30
+ app = read_text("app.py")
31
+
32
+ self.assertNotIn("multimodalart/wan-2-2-first-last-frame", app)
33
+ self.assertIn('VIDEO_SPACE_ID = os.environ.get("VIDEO_SPACE_ID", "").strip()', app)
34
+ self.assertIn("Client(VIDEO_SPACE_ID)", app)
35
+
36
+ def test_no_external_logo_image_or_sneak_moose_docs(self):
37
+ relevant_text = "\n".join(
38
+ read_text(path)
39
+ for path in ("app.py", "README.md", "AGENT.md", "CLAUDE.md")
40
+ )
41
+
42
+ self.assertNotIn("Sneak-Moose", relevant_text)
43
+ self.assertNotIn("qianwen-res.oss-cn-beijing.aliyuncs.com", relevant_text)
44
+ self.assertNotIn("Rapid-AIO v18", relevant_text)
45
+
46
+
47
+ if __name__ == "__main__":
48
+ unittest.main()