prithivMLmods commited on
Commit
8aa1b66
·
verified ·
1 Parent(s): 53fe147

update app

Browse files
Files changed (1) hide show
  1. app.py +231 -122
app.py CHANGED
@@ -3,31 +3,39 @@ import numpy as np
3
  import random
4
  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
  from gradio.themes import Soft
14
  from gradio.themes.utils import colors, fonts, sizes
15
- from typing import Iterable
16
-
17
- # --- Custom Theme Definition ---
18
- colors.orange_red = colors.Color(
19
- name="orange_red",
20
- c50="#FFF0E5", c100="#FFE0CC", c200="#FFC299", c300="#FFA366",
21
- c400="#FF8533", c500="#FF4500", c600="#E63E00", c700="#CC3700",
22
- c800="#B33000", c900="#992900", c950="#802200",
 
 
 
 
 
 
23
  )
24
 
25
- class OrangeRedTheme(Soft):
26
  def __init__(
27
  self,
28
  *,
29
  primary_hue: colors.Color | str = colors.gray,
30
- secondary_hue: colors.Color | str = colors.orange_red, # Use the new color
31
  neutral_hue: colors.Color | str = colors.slate,
32
  text_size: sizes.Size | str = sizes.text_lg,
33
  font: fonts.Font | str | Iterable[fonts.Font | str] = (
@@ -47,163 +55,264 @@ class OrangeRedTheme(Soft):
47
  )
48
  super().set(
49
  background_fill_primary="*primary_50",
 
 
 
 
 
50
  button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
51
  button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
52
- button_primary_text_color="white",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  )
54
 
55
- orange_red_theme = OrangeRedTheme()
56
-
57
 
58
- # --- Model Loading ---
 
59
  dtype = torch.bfloat16
60
  device = "cuda" if torch.cuda.is_available() else "cpu"
61
 
62
- pipe = QwenImageEditPlusPipeline.from_pretrained("Qwen/Qwen-Image-Edit-2509",
63
- transformer=QwenImageTransformer2DModel.from_pretrained("linoyts/Qwen-Image-Edit-Rapid-AIO",
64
- subfolder='transformer',
65
- torch_dtype=dtype,
66
- device_map='cuda'), torch_dtype=dtype).to(device)
67
- # Load all LoRA adapters
68
- pipe.load_lora_weights("dx8152/Qwen-Edit-2509-Multiple-angles", weight_name="镜头转换.safetensors", adapter_name="angles")
69
- pipe.load_lora_weights("dx8152/Qwen-Image-Edit-2509-Light_restoration", weight_name="移除光影.safetensors", adapter_name="light_restoration")
70
- pipe.load_lora_weights("autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime", weight_name="Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors", adapter_name="photo_to_anime")
71
- pipe.load_lora_weights("dx8152/Qwen-Image-Edit-2509-Relight", weight_name="Qwen-Edit-Relight.safetensors", adapter_name="relight")
 
 
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
 
74
  pipe.transformer.__class__ = QwenImageTransformer2DModel
75
  pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
76
-
77
  optimize_pipeline_(pipe, image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))], prompt="prompt")
78
 
79
 
80
- MAX_SEED = np.iinfo(np.int32).max
81
-
82
  @spaces.GPU
83
- def infer(input_image, prompt, lora_adapter, seed=42, randomize_seed=True, guidance_scale=1.0, steps=4, progress=gr.Progress(track_tqdm=True)):
84
- """
85
- Perform image editing based on the selected LoRA adapter and prompt.
86
- """
87
- if not input_image:
88
- raise gr.Error("Please upload an image for editing.")
89
-
90
- # Set the LoRA adapter based on user selection
91
- if lora_adapter == "Multiple Angles":
92
- pipe.set_adapters(["angles"], adapter_weights=[1.0])
93
- elif lora_adapter == "Light Restoration":
 
 
 
 
 
 
94
  pipe.set_adapters(["light_restoration"], adapter_weights=[1.0])
 
 
95
  elif lora_adapter == "Photo to Anime":
96
  pipe.set_adapters(["photo_to_anime"], adapter_weights=[1.0])
97
- elif lora_adapter == "Relight":
98
  pipe.set_adapters(["relight"], adapter_weights=[1.0])
99
 
100
  if randomize_seed:
101
  seed = random.randint(0, MAX_SEED)
102
-
103
  generator = torch.Generator(device=device).manual_seed(seed)
104
-
105
- original_image = input_image.copy().convert("RGB")
106
 
107
  result = pipe(
108
- image=original_image,
109
  prompt=prompt,
110
- height=original_image.size[1],
111
- width=original_image.size[0],
112
- num_inference_steps=steps,
113
  generator=generator,
114
- true_cfg_scale=guidance_scale,
115
  num_images_per_prompt=1,
116
  ).images[0]
117
 
118
- return result, seed, gr.Button(visible=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
 
 
 
 
 
 
 
 
 
120
 
121
- # --- UI ---
122
- css = """
123
- #col-container {
124
- margin: 0 auto;
125
- max-width: 960px;
 
126
  }
127
- #main-title h1 {font-size: 2.1em !important;}
128
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
- with gr.Blocks(css=css, theme=orange_red_theme) as demo:
131
  with gr.Column(elem_id="col-container"):
132
- gr.Markdown("# **Qwen-Image-Edit-2509-LoRAs-Fast**", elem_id="main-title")
133
- gr.Markdown("Image manipulation with Qwen Image Edit 2509 and various LoRA adapters.")
134
-
135
  with gr.Row():
136
- with gr.Column():
137
- input_image = gr.Image(label="Upload Image", type="pil", height=400)
138
- with gr.Row():
139
- prompt = gr.Text(
140
- label="Edit Prompt",
141
- show_label=False,
142
- max_lines=1,
143
- placeholder="Enter your prompt for editing",
144
- container=False,
145
- )
146
- run_button = gr.Button("Run", variant="primary", scale=0)
147
-
148
  lora_adapter = gr.Dropdown(
149
- label="Choose LoRA Adapter",
150
- choices=["Multiple Angles", "Light Restoration", "Photo to Anime", "Relight"],
151
- value="Multiple Angles"
 
 
 
 
 
 
 
 
 
 
 
152
  )
 
 
153
 
154
- with gr.Accordion("Advanced Settings", open=False):
155
- seed = gr.Slider(
156
- label="Seed",
157
- minimum=0,
158
- maximum=MAX_SEED,
159
- step=1,
160
- value=0,
161
- )
162
- randomize_seed = gr.Checkbox(label="Randomize seed", value=True)
163
- guidance_scale = gr.Slider(
164
- label="Guidance Scale",
165
- minimum=1.0,
166
- maximum=10.0,
167
- step=0.1,
168
- value=1.0,
169
- )
170
- steps = gr.Slider(
171
- label="Steps",
172
- minimum=1,
173
- maximum=30,
174
- value=4,
175
- step=1
176
- )
177
-
178
- with gr.Column():
179
- output_image = gr.Image(label="Output Image", show_label=True, interactive=False, format="png", height=400)
180
- reuse_button = gr.Button("Reuse this image", visible=False)
181
 
182
  gr.Examples(
 
183
  examples=[
184
- ["examples/sea.png", "Rotate the camera 90 degrees to the left.", "Multiple Angles"],
185
- ["examples/shadow.jpg", "Remove shadows and relight the image using soft lighting.", "Light Restoration"],
186
- ["examples/girl.jpg", "transform into anime", "Photo to Anime"],
187
- ["examples/dark.jpg", "Relight the image using soft, diffused lighting that simulates sunlight filtering through curtains.", "Relight"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  ],
189
- inputs=[input_image, prompt, lora_adapter],
190
- outputs=[output_image, seed],
191
  fn=infer,
192
- cache_examples="lazy",
193
- label="Examples"
194
  )
195
-
196
- gr.on(
197
- triggers=[run_button.click, prompt.submit],
198
- fn=infer,
199
- inputs=[input_image, prompt, lora_adapter, seed, randomize_seed, guidance_scale, steps],
200
- outputs=[output_image, seed, reuse_button]
201
  )
202
-
203
- reuse_button.click(
204
- fn=lambda image: image,
205
- inputs=[output_image],
206
- outputs=[input_image]
 
 
 
 
 
 
207
  )
208
 
209
- demo.launch(mcp_server=True, ssr_mode=False, show_error=True)
 
3
  import random
4
  import torch
5
  import spaces
6
+ import os
7
  from PIL import Image
8
+
9
  from diffusers import FlowMatchEulerDiscreteScheduler
10
  from optimization import optimize_pipeline_
11
  from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
12
  from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
13
  from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
14
+
15
  from gradio.themes import Soft
16
  from gradio.themes.utils import colors, fonts, sizes
17
+
18
+ colors.steel_blue = colors.Color(
19
+ name="steel_blue",
20
+ c50="#EBF3F8",
21
+ c100="#D3E5F0",
22
+ c200="#A8CCE1",
23
+ c300="#7DB3D2",
24
+ c400="#529AC3",
25
+ c500="#4682B4",
26
+ c600="#3E72A0",
27
+ c700="#36638C",
28
+ c800="#2E5378",
29
+ c900="#264364",
30
+ c950="#1E3450",
31
  )
32
 
33
+ class SteelBlueTheme(Soft):
34
  def __init__(
35
  self,
36
  *,
37
  primary_hue: colors.Color | str = colors.gray,
38
+ secondary_hue: colors.Color | str = colors.steel_blue,
39
  neutral_hue: colors.Color | str = colors.slate,
40
  text_size: sizes.Size | str = sizes.text_lg,
41
  font: fonts.Font | str | Iterable[fonts.Font | str] = (
 
55
  )
56
  super().set(
57
  background_fill_primary="*primary_50",
58
+ background_fill_primary_dark="*primary_900",
59
+ body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
60
+ body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
61
+ button_primary_text_color="white",
62
+ button_primary_text_color_hover="white",
63
  button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
64
  button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
65
+ button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_800)",
66
+ button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_500)",
67
+ button_secondary_text_color="black",
68
+ button_secondary_text_color_hover="white",
69
+ button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
70
+ button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
71
+ button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
72
+ button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
73
+ slider_color="*secondary_500",
74
+ slider_color_dark="*secondary_600",
75
+ block_title_text_weight="600",
76
+ block_border_width="3px",
77
+ block_shadow="*shadow_drop_lg",
78
+ button_primary_shadow="*shadow_drop_lg",
79
+ button_large_padding="11px",
80
+ color_accent_soft="*primary_100",
81
+ block_label_background_fill="*primary_200",
82
  )
83
 
84
+ steel_blue_theme = SteelBlueTheme()
 
85
 
86
+ # --- Constants and Setup ---
87
+ MAX_SEED = np.iinfo(np.int32).max
88
  dtype = torch.bfloat16
89
  device = "cuda" if torch.cuda.is_available() else "cpu"
90
 
91
+ # --- Model Loading ---
92
+ # Load the base pipeline and the optimized transformer
93
+ pipe = QwenImageEditPlusPipeline.from_pretrained(
94
+ "Qwen/Qwen-Image-Edit-2509",
95
+ transformer=QwenImageTransformer2DModel.from_pretrained(
96
+ "linoyts/Qwen-Image-Edit-Rapid-AIO",
97
+ subfolder='transformer',
98
+ torch_dtype=dtype,
99
+ device_map='cuda'
100
+ ),
101
+ torch_dtype=dtype
102
+ ).to(device)
103
 
104
+ # Load all LoRA adapters with unique names
105
+ pipe.load_lora_weights(
106
+ "dx8152/Qwen-Image-Edit-2509-Light_restoration",
107
+ weight_name="移除光影.safetensors",
108
+ adapter_name="light_restoration"
109
+ )
110
+ pipe.load_lora_weights(
111
+ "dx8152/Qwen-Edit-2509-Multiple-angles",
112
+ weight_name="镜头转换.safetensors",
113
+ adapter_name="multiple_angles"
114
+ )
115
+ pipe.load_lora_weights(
116
+ "autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime",
117
+ weight_name="Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors",
118
+ adapter_name="photo_to_anime"
119
+ )
120
+ pipe.load_lora_weights(
121
+ "dx8152/Qwen-Image-Edit-2509-Relight",
122
+ weight_name="Qwen-Edit-Relight.safetensors",
123
+ adapter_name="relight"
124
+ )
125
 
126
+ # Apply optimizations
127
  pipe.transformer.__class__ = QwenImageTransformer2DModel
128
  pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
 
129
  optimize_pipeline_(pipe, image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))], prompt="prompt")
130
 
131
 
132
+ # --- Inference Logic ---
 
133
  @spaces.GPU
134
+ def infer(
135
+ image,
136
+ prompt,
137
+ lora_adapter,
138
+ seed,
139
+ randomize_seed,
140
+ true_guidance_scale,
141
+ num_inference_steps,
142
+ height,
143
+ width,
144
+ progress=gr.Progress(track_tqdm=True)
145
+ ):
146
+ if image is None:
147
+ raise gr.Error("Please upload an image to get started.")
148
+
149
+ # Set the active LoRA adapter based on user selection
150
+ if lora_adapter == "Shadow/Light Restoration":
151
  pipe.set_adapters(["light_restoration"], adapter_weights=[1.0])
152
+ elif lora_adapter == "Multiple Angles":
153
+ pipe.set_adapters(["multiple_angles"], adapter_weights=[1.0])
154
  elif lora_adapter == "Photo to Anime":
155
  pipe.set_adapters(["photo_to_anime"], adapter_weights=[1.0])
156
+ elif lora_adapter == "Advanced Relighting":
157
  pipe.set_adapters(["relight"], adapter_weights=[1.0])
158
 
159
  if randomize_seed:
160
  seed = random.randint(0, MAX_SEED)
 
161
  generator = torch.Generator(device=device).manual_seed(seed)
 
 
162
 
163
  result = pipe(
164
+ image=image.convert("RGB"),
165
  prompt=prompt,
166
+ height=height,
167
+ width=width,
168
+ num_inference_steps=num_inference_steps,
169
  generator=generator,
170
+ true_cfg_scale=true_guidance_scale,
171
  num_images_per_prompt=1,
172
  ).images[0]
173
 
174
+ return result, seed
175
+
176
+ # --- UI Helper Functions ---
177
+ def update_dimensions_on_upload(image):
178
+ """Adjusts the height and width sliders to match the uploaded image's aspect ratio."""
179
+ if image is None:
180
+ return 1024, 1024
181
+
182
+ original_width, original_height = image.size
183
+
184
+ if original_width > original_height:
185
+ new_width = 1024
186
+ new_height = int(new_width * (original_height / original_width))
187
+ else:
188
+ new_height = 1024
189
+ new_width = int(new_height * (original_width / original_height))
190
+
191
+ # Ensure dimensions are multiples of 8 for model compatibility
192
+ new_width = (new_width // 8) * 8
193
+ new_height = (new_height // 8) * 8
194
+
195
+ return new_width, new_height
196
 
197
+ def update_prompt_on_adapter_change(adapter_name):
198
+ """Provides a suggested prompt when a new adapter is selected."""
199
+ prompts = {
200
+ "Shadow/Light Restoration": "Remove shadows and relight the image using soft lighting.",
201
+ "Multiple Angles": "A photo of the scene from a top-down view.",
202
+ "Photo to Anime": "Transform into anime, masterpiece, best quality.",
203
+ "Advanced Relighting": "Relight the image using soft, diffused lighting that simulates sunlight filtering through curtains."
204
+ }
205
+ return prompts.get(adapter_name, "")
206
 
207
+ # --- Gradio UI ---
208
+ css = '''
209
+ #col-container {
210
+ max-width: 960px;
211
+ margin: 0 auto;
212
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
213
  }
214
+ .dark .progress-text { color: white !important }
215
+ #examples { max-width: 960px; margin: 0 auto; }
216
+ .gradio-container {
217
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
218
+ }
219
+ .gr-button-primary {
220
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
221
+ border: none !important;
222
+ border-radius: 12px !important;
223
+ padding: 12px 24px !important;
224
+ font-weight: 600 !important;
225
+ }
226
+ .gr-box {
227
+ border-radius: 16px !important;
228
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important;
229
+ }
230
+ '''
231
 
232
+ with gr.Blocks(theme=steel_blue_theme, css=css) as demo:
233
  with gr.Column(elem_id="col-container"):
234
+ gr.Markdown("# Qwen Image Edit - Fast LoRA")
 
 
235
  with gr.Row():
236
+ with gr.Column(scale=1):
237
+ image = gr.Image(label="Upload Image", type="pil", height=450)
238
+
 
 
 
 
 
 
 
 
 
239
  lora_adapter = gr.Dropdown(
240
+ label="Choose an Editing Tool",
241
+ choices=[
242
+ "Shadow/Light Restoration",
243
+ "Multiple Angles",
244
+ "Photo to Anime",
245
+ "Advanced Relighting"
246
+ ],
247
+ value="Shadow/Light Restoration"
248
+ )
249
+
250
+ prompt = gr.Textbox(
251
+ label="Prompt",
252
+ value="Remove shadows and relight the image using soft lighting.",
253
+ lines=2
254
  )
255
+
256
+ run_btn = gr.Button("Generate", variant="primary", size="lg")
257
 
258
+ with gr.Accordion("⚙️ Advanced Settings", open=False):
259
+ seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0)
260
+ randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
261
+ true_guidance_scale = gr.Slider(label="Guidance Scale", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
262
+ num_inference_steps = gr.Slider(label="Inference Steps", minimum=1, maximum=40, step=1, value=4)
263
+ height = gr.Slider(label="Height", minimum=256, maximum=2048, step=8, value=1024)
264
+ width = gr.Slider(label="Width", minimum=256, maximum=2048, step=8, value=1024)
265
+
266
+ with gr.Column(scale=1):
267
+ result = gr.Image(label="Output Image", interactive=False, height=500, format="png")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
  gr.Examples(
270
+ elem_id="examples",
271
  examples=[
272
+ [
273
+ "examples/example1.png",
274
+ "A photo of the scene from a low angle shot.",
275
+ "Multiple Angles",
276
+ ],
277
+ [
278
+ "examples/example2.png",
279
+ "Remove shadows and relight the image using soft lighting.",
280
+ "Shadow/Light Restoration",
281
+ ],
282
+ [
283
+ "examples/example3.png",
284
+ "Transform into anime, masterpiece, best quality, girl with cherry blossoms.",
285
+ "Photo to Anime",
286
+ ],
287
+ [
288
+ "examples/example4.png",
289
+ "Relight the image using soft, diffused lighting that simulates sunlight filtering through curtains.",
290
+ "Advanced Relighting",
291
+ ],
292
  ],
293
+ inputs=[image, prompt, lora_adapter],
294
+ outputs=[result, seed],
295
  fn=infer,
296
+ cache_examples=False
 
297
  )
298
+
299
+ # --- Event Handlers ---
300
+ run_btn.click(
301
+ fn=infer,
302
+ inputs=[image, prompt, lora_adapter, seed, randomize_seed, true_guidance_scale, num_inference_steps, height, width],
303
+ outputs=[result, seed]
304
  )
305
+
306
+ image.upload(
307
+ fn=update_dimensions_on_upload,
308
+ inputs=[image],
309
+ outputs=[width, height]
310
+ )
311
+
312
+ lora_adapter.change(
313
+ fn=update_prompt_on_adapter_change,
314
+ inputs=[lora_adapter],
315
+ outputs=[prompt]
316
  )
317
 
318
+ demo.launch(mcp_server=False)