Spaces:
Runtime error
Runtime error
update
Browse files- README.md +16 -16
- app.py +78 -99
- requirements.txt +3 -4
README.md
CHANGED
|
@@ -1,30 +1,30 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: purple
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 4.0.2
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
| 11 |
-
# Ứng dụng tạo video từ hình ảnh
|
| 12 |
|
| 13 |
-
Ứng dụng
|
| 14 |
|
| 15 |
-
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
|
|
|
| 19 |
3. Điều chỉnh các tham số (tùy chọn)
|
| 20 |
4. Nhấn "Tạo video"
|
| 21 |
-
5. Đợi quá trình xử lý hoàn tất và tải xuống video
|
| 22 |
-
|
| 23 |
-
## Mô hình được sử dụng
|
| 24 |
-
|
| 25 |
-
Ứng dụng này sử dụng mô hình AnimateDiff để tạo hoạt ảnh từ hình ảnh tĩnh.
|
| 26 |
|
| 27 |
-
##
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
-
|
| 30 |
-
-
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Animate Person From Image
|
| 3 |
+
emoji: 🎭
|
| 4 |
+
colorFrom: pink
|
| 5 |
colorTo: purple
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 4.0.2
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
---
|
|
|
|
| 11 |
|
| 12 |
+
# Ứng dụng tạo video người chuyển động từ ảnh
|
| 13 |
|
| 14 |
+
Ứng dụng này sử dụng AI để tạo video người chuyển động từ một ảnh tĩnh.
|
| 15 |
|
| 16 |
+
## Cách sử dụng
|
| 17 |
+
1. Tải lên ảnh chứa người
|
| 18 |
+
2. Nhập mô tả cho kiểu chuyển động mong muốn
|
| 19 |
3. Điều chỉnh các tham số (tùy chọn)
|
| 20 |
4. Nhấn "Tạo video"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
## Các tham số
|
| 23 |
+
- **Mô tả chuyển động**: Mô tả bằng lời cách người sẽ chuyển động
|
| 24 |
+
- **Mức độ chuyển động**: Điều chỉnh cường độ chuyển động (1-255)
|
| 25 |
+
- **FPS**: Số khung hình mỗi giây của video kết quả
|
| 26 |
|
| 27 |
+
## Mẹo sử dụng
|
| 28 |
+
- Ảnh nên có nền đơn giản để có kết quả tốt nhất
|
| 29 |
+
- Người trong ảnh nên ở tư thế tự nhiên, không quá phức tạp
|
| 30 |
+
- Thử nghiệm với các mô tả khác nhau để có hiệu quả tốt nhất
|
app.py
CHANGED
|
@@ -1,130 +1,109 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
import torch
|
| 3 |
import os
|
| 4 |
-
|
| 5 |
import numpy as np
|
| 6 |
-
import
|
| 7 |
-
import
|
|
|
|
| 8 |
|
| 9 |
-
#
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
-
# Hàm tạo video
|
| 14 |
-
def
|
| 15 |
if image is None:
|
| 16 |
-
return None, "Vui lòng tải lên một hình ảnh"
|
| 17 |
-
|
| 18 |
-
# Đảm bảo hình ảnh là định dạng RGB
|
| 19 |
-
if image.mode != "RGB":
|
| 20 |
-
image = image.convert("RGB")
|
| 21 |
-
|
| 22 |
-
# Thay đổi kích thước hình ảnh về 512x512
|
| 23 |
-
image = image.resize((512, 512))
|
| 24 |
|
| 25 |
try:
|
| 26 |
-
#
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
if effect_type == "zoom-in":
|
| 30 |
-
# Hiệu ứng zoom-in
|
| 31 |
-
for i in range(num_frames):
|
| 32 |
-
zoom_factor = 1.0 + (i / num_frames) * (effect_strength / 10)
|
| 33 |
-
img_copy = image.copy()
|
| 34 |
-
|
| 35 |
-
size = int(img_copy.width / zoom_factor)
|
| 36 |
-
left = (img_copy.width - size) // 2
|
| 37 |
-
top = (img_copy.height - size) // 2
|
| 38 |
-
right = left + size
|
| 39 |
-
bottom = top + size
|
| 40 |
-
|
| 41 |
-
cropped = img_copy.crop((left, top, right, bottom))
|
| 42 |
-
frame = cropped.resize((512, 512))
|
| 43 |
-
frames.append(np.array(frame))
|
| 44 |
-
|
| 45 |
-
elif effect_type == "pan-right":
|
| 46 |
-
# Hiệu ứng pan từ trái sang phải
|
| 47 |
-
width, height = image.size
|
| 48 |
-
for i in range(num_frames):
|
| 49 |
-
offset = int((i / num_frames) * width * (effect_strength / 10))
|
| 50 |
-
img_copy = image.copy()
|
| 51 |
-
|
| 52 |
-
# Tạo hiệu ứng pan
|
| 53 |
-
if offset > 0:
|
| 54 |
-
# Lấy phần hình ảnh từ offset đến cuối
|
| 55 |
-
right_part = img_copy.crop((offset, 0, width, height))
|
| 56 |
-
# Lấy phần còn lại từ đầu
|
| 57 |
-
left_part = img_copy.crop((0, 0, offset, height))
|
| 58 |
-
# Tạo hình ảnh mới
|
| 59 |
-
new_img = Image.new('RGB', (width, height))
|
| 60 |
-
new_img.paste(right_part, (0, 0))
|
| 61 |
-
new_img.paste(left_part, (width - offset, 0))
|
| 62 |
-
frames.append(np.array(new_img))
|
| 63 |
-
else:
|
| 64 |
-
frames.append(np.array(img_copy))
|
| 65 |
-
|
| 66 |
-
elif effect_type == "fade":
|
| 67 |
-
# Hiệu ứng fade in/out
|
| 68 |
-
base_frame = np.array(image)
|
| 69 |
-
for i in range(num_frames):
|
| 70 |
-
# Tính toán độ mờ
|
| 71 |
-
if i < num_frames / 2:
|
| 72 |
-
# Fade in
|
| 73 |
-
alpha = i / (num_frames / 2)
|
| 74 |
-
else:
|
| 75 |
-
# Fade out
|
| 76 |
-
alpha = 2.0 - (i / (num_frames / 2))
|
| 77 |
-
|
| 78 |
-
# Áp dụng độ mờ
|
| 79 |
-
frame = (base_frame * alpha * (effect_strength / 5)).astype(np.uint8)
|
| 80 |
-
frames.append(frame)
|
| 81 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
else:
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
frame = (np.array(image) * brightness).clip(0, 255).astype(np.uint8)
|
| 87 |
-
frames.append(frame)
|
| 88 |
|
| 89 |
-
# Tạo
|
| 90 |
-
|
| 91 |
-
imageio.mimsave(output_path, frames, fps=8)
|
| 92 |
|
| 93 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
except Exception as e:
|
| 95 |
return None, f"Lỗi: {str(e)}"
|
| 96 |
|
| 97 |
# Tạo giao diện Gradio
|
| 98 |
-
with gr.Blocks(title="
|
| 99 |
-
gr.Markdown("# Tạo video
|
| 100 |
-
gr.Markdown("Tải lên
|
| 101 |
|
| 102 |
with gr.Row():
|
| 103 |
with gr.Column():
|
| 104 |
-
image_input = gr.Image(type="pil", label="Tải lên
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
value="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
)
|
| 110 |
-
num_frames = gr.Slider(minimum=10, maximum=30, value=16, step=2, label="Số khung hình")
|
| 111 |
-
effect_strength = gr.Slider(minimum=1.0, maximum=10.0, value=5.0, step=0.5, label="Độ mạnh của hiệu ứng")
|
| 112 |
submit_btn = gr.Button("Tạo video")
|
| 113 |
-
|
| 114 |
with gr.Column():
|
| 115 |
output_video = gr.Video(label="Video kết quả")
|
| 116 |
output_message = gr.Textbox(label="Thông báo")
|
| 117 |
|
| 118 |
submit_btn.click(
|
| 119 |
-
fn=
|
| 120 |
-
inputs=[image_input,
|
| 121 |
outputs=[output_video, output_message]
|
| 122 |
)
|
| 123 |
|
| 124 |
-
gr.Markdown("###
|
| 125 |
-
gr.Markdown("-
|
| 126 |
-
gr.Markdown("-
|
| 127 |
-
gr.Markdown("-
|
| 128 |
-
gr.Markdown("- **Pulse**: Hiệu ứng thay đổi độ sáng theo nhịp")
|
| 129 |
|
| 130 |
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
|
|
|
| 2 |
import os
|
| 3 |
+
import torch
|
| 4 |
import numpy as np
|
| 5 |
+
from PIL import Image
|
| 6 |
+
from diffusers import DiffusionPipeline, DDIMScheduler
|
| 7 |
+
from diffusers.utils import export_to_video
|
| 8 |
|
| 9 |
+
# Khởi tạo mô hình
|
| 10 |
+
def load_model():
|
| 11 |
+
pipe = DiffusionPipeline.from_pretrained(
|
| 12 |
+
"guoyww/animatediff-motion-adapter-v1-5",
|
| 13 |
+
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
|
| 14 |
+
)
|
| 15 |
+
pipe.scheduler = DDIMScheduler.from_config(pipe.scheduler.config)
|
| 16 |
+
pipe = pipe.to("cuda" if torch.cuda.is_available() else "cpu")
|
| 17 |
+
return pipe
|
| 18 |
|
| 19 |
+
# Hàm xử lý chính để tạo video từ ảnh
|
| 20 |
+
def animate_person(image, prompt, motion_bucket_id=127, fps=8):
|
| 21 |
if image is None:
|
| 22 |
+
return None, "Vui lòng tải lên một hình ảnh."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
|
| 24 |
try:
|
| 25 |
+
# Xử lý và chuẩn bị hình ảnh
|
| 26 |
+
if image.mode != "RGB":
|
| 27 |
+
image = image.convert("RGB")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
# Resize hình ảnh để phù hợp với mô hình
|
| 30 |
+
w, h = image.size
|
| 31 |
+
if w > h:
|
| 32 |
+
new_w, new_h = 512, int(h * 512 / w)
|
| 33 |
else:
|
| 34 |
+
new_w, new_h = int(w * 512 / h), 512
|
| 35 |
+
|
| 36 |
+
image = image.resize((new_w, new_h))
|
|
|
|
|
|
|
| 37 |
|
| 38 |
+
# Tạo mặt nạ để tập trung vào chủ thể (người)
|
| 39 |
+
# Mặt nạ đơn giản - trong thực tế có thể cần mô hình phân đoạn người phức tạp hơn
|
|
|
|
| 40 |
|
| 41 |
+
# Tải mô hình
|
| 42 |
+
pipe = load_model()
|
| 43 |
+
|
| 44 |
+
# Tạo video
|
| 45 |
+
if not prompt:
|
| 46 |
+
prompt = "A person moving naturally, photorealistic, high quality"
|
| 47 |
+
|
| 48 |
+
# Thêm hướng dẫn về chuyển động người để có kết quả tốt hơn
|
| 49 |
+
full_prompt = f"{prompt}, person in motion, smooth movement, natural pose, high quality, detailed"
|
| 50 |
+
|
| 51 |
+
# Sinh video
|
| 52 |
+
output = pipe(
|
| 53 |
+
prompt=full_prompt,
|
| 54 |
+
image=image,
|
| 55 |
+
negative_prompt="blurry, low quality, distorted, disfigured, bad anatomy",
|
| 56 |
+
num_frames=24,
|
| 57 |
+
guidance_scale=7.5,
|
| 58 |
+
num_inference_steps=50,
|
| 59 |
+
motion_bucket_id=motion_bucket_id
|
| 60 |
+
)
|
| 61 |
+
|
| 62 |
+
# Xuất video
|
| 63 |
+
video_path = "animated_person.mp4"
|
| 64 |
+
frames = output.frames[0]
|
| 65 |
+
export_to_video(frames, video_path, fps=fps)
|
| 66 |
+
|
| 67 |
+
return video_path, "Video được tạo thành công!"
|
| 68 |
except Exception as e:
|
| 69 |
return None, f"Lỗi: {str(e)}"
|
| 70 |
|
| 71 |
# Tạo giao diện Gradio
|
| 72 |
+
with gr.Blocks(title="Tạo video người chuyển động từ ảnh") as demo:
|
| 73 |
+
gr.Markdown("# Tạo video người chuyển động từ ảnh")
|
| 74 |
+
gr.Markdown("Tải lên ảnh người và xem họ chuyển động tự nhiên trong video")
|
| 75 |
|
| 76 |
with gr.Row():
|
| 77 |
with gr.Column():
|
| 78 |
+
image_input = gr.Image(type="pil", label="Tải lên ảnh người")
|
| 79 |
+
prompt_input = gr.Textbox(
|
| 80 |
+
label="Mô tả chuyển động",
|
| 81 |
+
placeholder="Mô tả cách người trong ảnh sẽ chuyển động...",
|
| 82 |
+
value="Person walking naturally, photorealistic"
|
| 83 |
+
)
|
| 84 |
+
motion_input = gr.Slider(
|
| 85 |
+
minimum=1, maximum=255, value=127, step=1,
|
| 86 |
+
label="Mức độ chuyển động (1-255)"
|
| 87 |
+
)
|
| 88 |
+
fps_input = gr.Slider(
|
| 89 |
+
minimum=6, maximum=30, value=8, step=1,
|
| 90 |
+
label="Số khung hình mỗi giây (FPS)"
|
| 91 |
)
|
|
|
|
|
|
|
| 92 |
submit_btn = gr.Button("Tạo video")
|
| 93 |
+
|
| 94 |
with gr.Column():
|
| 95 |
output_video = gr.Video(label="Video kết quả")
|
| 96 |
output_message = gr.Textbox(label="Thông báo")
|
| 97 |
|
| 98 |
submit_btn.click(
|
| 99 |
+
fn=animate_person,
|
| 100 |
+
inputs=[image_input, prompt_input, motion_input, fps_input],
|
| 101 |
outputs=[output_video, output_message]
|
| 102 |
)
|
| 103 |
|
| 104 |
+
gr.Markdown("### Lưu ý")
|
| 105 |
+
gr.Markdown("- Quá trình tạo video có thể mất vài phút")
|
| 106 |
+
gr.Markdown("- Kết quả tốt nhất với ảnh người rõ nét, chụp thẳng")
|
| 107 |
+
gr.Markdown("- Sử dụng prompt cụ thể để điều khiển kiểu chuyển động")
|
|
|
|
| 108 |
|
| 109 |
demo.launch()
|
requirements.txt
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
gradio==4.0.2
|
| 2 |
torch
|
| 3 |
torchvision
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
imageio-ffmpeg
|
|
|
|
| 1 |
gradio==4.0.2
|
| 2 |
torch
|
| 3 |
torchvision
|
| 4 |
+
diffusers>=0.24.0
|
| 5 |
+
transformers>=4.31.0
|
| 6 |
+
accelerate
|
|
|