prithivMLmods commited on
Commit
d070580
·
verified ·
1 Parent(s): 99e8ea7

Update app.py

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