dream2589632147 commited on
Commit
8aa34e2
·
verified ·
1 Parent(s): 0fcba18

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -375
app.py DELETED
@@ -1,375 +0,0 @@
1
- import gradio as gr
2
- import numpy as np
3
- import random
4
- import torch
5
- import spaces
6
- from PIL import Image
7
- from diffusers import FlowMatchEulerDiscreteScheduler
8
- from optimization import optimize_pipeline_
9
- from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
10
- from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
11
- from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
12
- import math
13
- from huggingface_hub import hf_hub_download
14
- from safetensors.torch import load_file
15
- from PIL import Image
16
- import os
17
- import gradio as gr
18
- from gradio_client import Client, handle_file
19
- import tempfile
20
- from typing import Optional, Tuple, Any
21
-
22
- # --- Model Loading ---
23
- dtype = torch.bfloat16
24
- device = "cuda" if torch.cuda.is_available() else "cpu"
25
- pipe = QwenImageEditPlusPipeline.from_pretrained(
26
- "Qwen/Qwen-Image-Edit-2509",
27
- transformer=QwenImageTransformer2DModel.from_pretrained(
28
- "linoyts/Qwen-Image-Edit-Rapid-AIO",
29
- subfolder='transformer',
30
- torch_dtype=dtype,
31
- device_map='cuda'
32
- ),
33
- torch_dtype=dtype
34
- ).to(device)
35
- pipe.load_lora_weights(
36
- "dx8152/Qwen-Edit-2509-Multiple-angles",
37
- weight_name="镜头转换.safetensors",
38
- adapter_name="angles"
39
- )
40
- pipe.set_adapters(["angles"], adapter_weights=[1.])
41
- pipe.fuse_lora(adapter_names=["angles"], lora_scale=1.25)
42
- pipe.unload_lora_weights()
43
- pipe.transformer.__class__ = QwenImageTransformer2DModel
44
- pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
45
- optimize_pipeline_(
46
- pipe,
47
- image=[Image.new("RGB", (1024, 1024)), Image.new("RGB", (1024, 1024))],
48
- prompt="prompt"
49
- )
50
-
51
- MAX_SEED = np.iinfo(np.int32).max
52
-
53
- def _generate_video_segment(
54
- input_image_path: str,
55
- output_image_path: str,
56
- prompt: str,
57
- request: gr.Request,
58
- duration: float = 5.0 # مدة الفيديو بالثواني
59
- ) -> str:
60
- """
61
- Generate a video segment of exact duration using Wan 2.2.
62
- Duration is controlled via num_frames = duration * fps.
63
- """
64
- x_ip_token = request.headers.get('x-ip-token', None)
65
- headers = {"x-ip-token": x_ip_token} if x_ip_token else {}
66
-
67
- video_client = Client(
68
- "multimodalart/wan-2-2-first-last-frame",
69
- headers=headers
70
- )
71
-
72
- fps = 25
73
- num_frames = max(1, int(duration * fps)) # ضمان عدد صحيح وإيجابي
74
-
75
- try:
76
- result = video_client.predict(
77
- start_image_pil=handle_file(input_image_path),
78
- end_image_pil=handle_file(output_image_path),
79
- prompt=prompt or "Camera movement transformation",
80
- num_frames=num_frames,
81
- fps=fps,
82
- motion="medium", # يمكنك تغييره إلى "high" لسلاسة أكبر
83
- api_name="/generate_video",
84
- )
85
- return result[0]["video"]
86
- except Exception as e:
87
- raise gr.Error(f"فشل إنشاء الفيديو: {str(e)}")
88
-
89
- def build_camera_prompt(
90
- rotate_deg: float = 0.0,
91
- move_forward: float = 0.0,
92
- vertical_tilt: float = 0.0,
93
- wideangle: bool = False
94
- ) -> str:
95
- prompt_parts = []
96
- if rotate_deg != 0:
97
- direction = "left" if rotate_deg > 0 else "right"
98
- abs_deg = abs(rotate_deg)
99
- prompt_parts.append(
100
- f"将镜头向{direction}旋转{abs_deg}度 Rotate the camera {abs_deg} degrees to the {direction}."
101
- )
102
- if move_forward > 5:
103
- prompt_parts.append("将镜头转为特写镜头 Turn the camera to a close-up.")
104
- elif move_forward >= 1:
105
- prompt_parts.append("将镜头向前移动 Move the camera forward.")
106
- if vertical_tilt <= -1:
107
- prompt_parts.append("将相机转向鸟瞰视角 Turn the camera to a bird's-eye view.")
108
- elif vertical_tilt >= 1:
109
- prompt_parts.append("将相机切换到仰视视角 Turn the camera to a worm's-eye view.")
110
- if wideangle:
111
- prompt_parts.append(" 将镜头转为广角镜头 Turn the camera to a wide-angle lens.")
112
- final_prompt = " ".join(prompt_parts).strip()
113
- return final_prompt if final_prompt else "no camera movement"
114
-
115
- @spaces.GPU
116
- def infer_camera_edit(
117
- image: Optional[Image.Image] = None,
118
- rotate_deg: float = 0.0,
119
- move_forward: float = 0.0,
120
- vertical_tilt: float = 0.0,
121
- wideangle: bool = False,
122
- seed: int = 0,
123
- randomize_seed: bool = True,
124
- true_guidance_scale: float = 1.0,
125
- num_inference_steps: int = 4,
126
- height: Optional[int] = None,
127
- width: Optional[int] = None,
128
- prev_output: Optional[Image.Image] = None,
129
- ) -> Tuple[Image.Image, int, str]:
130
- progress = gr.Progress(track_tqdm=True)
131
-
132
- prompt = build_camera_prompt(rotate_deg, move_forward, vertical_tilt, wideangle)
133
- print(f"Generated Prompt: {prompt}")
134
-
135
- if randomize_seed:
136
- seed = random.randint(0, MAX_SEED)
137
- generator = torch.Generator(device=device).manual_seed(seed)
138
-
139
- pil_images = []
140
- if image is not None:
141
- if isinstance(image, Image.Image):
142
- pil_images.append(image.convert("RGB"))
143
- elif hasattr(image, "name"):
144
- pil_images.append(Image.open(image.name).convert("RGB"))
145
- elif prev_output:
146
- pil_images.append(prev_output.convert("RGB"))
147
-
148
- if len(pil_images) == 0:
149
- raise gr.Error("يرجى رفع صورة أولاً.")
150
-
151
- if prompt == "no camera movement":
152
- return pil_images[0], seed, prompt
153
-
154
- result = pipe(
155
- image=pil_images,
156
- prompt=prompt,
157
- height=height if height != 0 else None,
158
- width=width if width != 0 else None,
159
- num_inference_steps=num_inference_steps,
160
- generator=generator,
161
- true_cfg_scale=true_guidance_scale,
162
- num_images_per_prompt=1,
163
- ).images[0]
164
- return result, seed, prompt
165
-
166
- def create_video_between_images(
167
- input_image: Optional[Image.Image],
168
- output_image: Optional[np.ndarray],
169
- prompt: str,
170
- request: gr.Request,
171
- video_duration: float = 5.0
172
- ) -> str:
173
- if input_image is None or output_image is None:
174
- raise gr.Error("كلا الصورتين مطلوبة لإنشاء الفيديو.")
175
-
176
- try:
177
- with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
178
- input_image.save(tmp.name)
179
- input_image_path = tmp.name
180
- output_pil = Image.fromarray(output_image.astype('uint8'))
181
- with tempfile.NamedTemporaryFile(delete=False, suffix=".png") as tmp:
182
- output_pil.save(tmp.name)
183
- output_image_path = tmp.name
184
-
185
- video_path = _generate_video_segment(
186
- input_image_path,
187
- output_image_path,
188
- prompt,
189
- request,
190
- duration=video_duration
191
- )
192
- # تنظيف الملفات المؤقتة
193
- os.unlink(input_image_path)
194
- os.unlink(output_image_path)
195
- return video_path
196
- except Exception as e:
197
- raise gr.Error(f"فشل إنشاء الفيديو: {e}")
198
-
199
- # --- UI ---
200
- css = '''#col-container { max-width: 900px; margin: 0 auto; }
201
- .dark .progress-text{color: white !important}
202
- #examples{max-width: 900px; margin: 0 auto; }'''
203
-
204
- def reset_all() -> list:
205
- return [0, 0, 0, False, 5.0, False, True] # أضفنا video_duration
206
-
207
- def end_reset() -> bool:
208
- return False
209
-
210
- def update_dimensions_on_upload(image: Optional[Image.Image]) -> Tuple[int, int]:
211
- if image is None:
212
- return 1024, 1024
213
- original_width, original_height = image.size
214
- if original_width > original_height:
215
- new_width = 1024
216
- new_height = int(new_width * (original_height / original_width))
217
- else:
218
- new_height = 1024
219
- new_width = int(new_height * (original_width / original_height))
220
- new_width = (new_width // 8) * 8
221
- new_height = (new_height // 8) * 8
222
- return new_width, new_height
223
-
224
- with gr.Blocks(theme=gr.themes.Citrus(), css=css) as demo:
225
- with gr.Column(elem_id="col-container"):
226
- gr.Markdown("## 🎬 Qwen Image Edit — Camera Angle Control + Video")
227
- gr.Markdown("""
228
- **تحكم كامل في زاوية الكاميرا + فيديو انتقالي بدقة 5 ثواني**
229
- باستخدام LoRA متعدد الزوايا + Wan 2.2
230
- """)
231
-
232
- with gr.Row():
233
- with gr.Column(scale=1):
234
- image = gr.Image(label="الصورة الأصلية", type="pil")
235
- prev_output = gr.Image(value=None, visible=False)
236
- is_reset = gr.Checkbox(value=False, visible=False)
237
-
238
- with gr.Tab("التحكم في الكاميرا"):
239
- rotate_deg = gr.Slider(label="تدوير (يمين/يسار) °", minimum=-90, maximum=90, step=45, value=0)
240
- move_forward = gr.Slider(label="تحريك للأمام → تكبير", minimum=0, maximum=10, step=5, value=0)
241
- vertical_tilt = gr.Slider(label="زاوية عمودية (طائر ↔ دودة)", minimum=-1, maximum=1, step=1, value=0)
242
- wideangle = gr.Checkbox(label="عدسة واسعة الزاوية", value=False)
243
-
244
- with gr.Row():
245
- reset_btn = gr.Button("إعادة تعيين")
246
- run_btn = gr.Button("توليد", variant="primary")
247
-
248
- with gr.Accordion("إعدادات متقدمة", open=False):
249
- seed = gr.Slider(label="البذرة", minimum=0, maximum=MAX_SEED, step=1, value=0)
250
- randomize_seed = gr.Checkbox(label="بذرة عشوائية", value=True)
251
- true_guidance_scale = gr.Slider(label="مقياس التوجيه", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
252
- num_inference_steps = gr.Slider(label="خطوات الاستدلال", minimum=1, maximum=40, step=1, value=4)
253
- height = gr.Slider(label="الارتفاع", minimum=256, maximum=2048, step=8, value=1024)
254
- width = gr.Slider(label="العرض", minimum=256, maximum=2048, step=8, value=1024)
255
-
256
- with gr.Column(scale=1):
257
- result = gr.Image(label="الصورة المعدلة", interactive=False)
258
- prompt_preview = gr.Textbox(label="النص المُرسل", interactive=False)
259
-
260
- with gr.Accordion("إنشاء فيديو انتقالي", open=True):
261
- video_duration = gr.Slider(
262
- label="مدة الفيديو (ثواني)",
263
- minimum=1, maximum=10, step=0.5, value=5.0
264
- )
265
- create_video_button = gr.Button(
266
- "🎥 إنشاء فيديو (5 ثواني)",
267
- variant="secondary",
268
- visible=False
269
- )
270
- video_output = gr.Video(
271
- label="الفيديو الناتج",
272
- show_download_button=True,
273
- autoplay=True,
274
- height=500
275
- )
276
-
277
- # --- Events ---
278
- inputs = [
279
- image, rotate_deg, move_forward, vertical_tilt, wideangle,
280
- seed, randomize_seed, true_guidance_scale, num_inference_steps,
281
- height, width, prev_output
282
- ]
283
- outputs = [result, seed, prompt_preview]
284
-
285
- # Reset
286
- reset_btn.click(
287
- fn=reset_all,
288
- inputs=None,
289
- outputs=[rotate_deg, move_forward, vertical_tilt, wideangle, video_duration, is_reset],
290
- queue=False
291
- ).then(fn=end_reset, inputs=None, outputs=[is_reset], queue=False)
292
-
293
- # Generate + show video button
294
- def infer_and_show(*args):
295
- result_img, result_seed, result_prompt = infer_camera_edit(*args)
296
- show_button = args[0] is not None
297
- return result_img, result_seed, result_prompt, gr.update(visible=show_button)
298
-
299
- run_event = run_btn.click(
300
- fn=infer_and_show,
301
- inputs=inputs,
302
- outputs=outputs + [create_video_button]
303
- )
304
-
305
- # Create video
306
- create_video_button.click(
307
- fn=lambda: gr.update(visible=True),
308
- outputs=video_output,
309
- api_name=False
310
- ).then(
311
- fn=create_video_between_images,
312
- inputs=[image, result, prompt_preview, gr.Request(), video_duration],
313
- outputs=video_output,
314
- api_name="create_video"
315
- )
316
-
317
- # Examples
318
- gr.Examples(
319
- examples=[
320
- ["tool_of_the_sea.png", 90, 0, 0, False, 0, True, 1.0, 4, 568, 1024],
321
- ["monkey.jpg", -90, 0, 0, False, 0, True, 1.0, 4, 704, 1024],
322
- ["metropolis.jpg", 0, 0, -1, False, 0, True, 1.0, 4, 816, 1024],
323
- ["disaster_girl.jpg", -45, 0, 1, False, 0, True, 1.0, 4, 768, 1024],
324
- ["grumpy.png", 90, 0, 1, False, 0, True, 1.0, 4, 576, 1024]
325
- ],
326
- inputs=[
327
- image, rotate_deg, move_forward, vertical_tilt, wideangle,
328
- seed, randomize_seed, true_guidance_scale, num_inference_steps, height, width
329
- ],
330
- outputs=outputs,
331
- fn=infer_camera_edit,
332
- cache_examples="lazy",
333
- elem_id="examples"
334
- )
335
-
336
- # Upload → update size + reset
337
- image.upload(
338
- fn=update_dimensions_on_upload,
339
- inputs=[image],
340
- outputs=[width, height]
341
- ).then(
342
- fn=reset_all,
343
- inputs=None,
344
- outputs=[rotate_deg, move_forward, vertical_tilt, wideangle, video_duration, is_reset],
345
- queue=False
346
- ).then(
347
- fn=end_reset,
348
- inputs=None,
349
- outputs=[is_reset],
350
- queue=False
351
- )
352
-
353
- # Live inference
354
- def maybe_infer(is_reset: bool, *args):
355
- if is_reset:
356
- return [gr.update()] * 4
357
- result_img, result_seed, result_prompt = infer_camera_edit(*args)
358
- show_button = args[0] is not None
359
- return result_img, result_seed, result_prompt, gr.update(visible=show_button)
360
-
361
- control_inputs_with_flag = [is_reset] + inputs
362
- for control in [rotate_deg, move_forward, vertical_tilt, wideangle, video_duration]:
363
- control.change(
364
- fn=maybe_infer,
365
- inputs=control_inputs_with_flag,
366
- outputs=outputs + [create_video_button]
367
- )
368
-
369
- run_event.then(lambda img, *_: img, inputs=[result], outputs=[prev_output])
370
-
371
- # API
372
- gr.api(infer_camera_edit, api_name="infer_edit_camera_angles")
373
- gr.api(create_video_between_images, api_name="create_video_between_images")
374
-
375
- demo.launch(mcp_server=True, show_api=True)