Update app.py
Browse files
app.py
CHANGED
|
@@ -45,8 +45,8 @@ upscaler_path = hf_hub_download(
|
|
| 45 |
)
|
| 46 |
CFG["spatial_upscaler_model_path"] = upscaler_path
|
| 47 |
|
| 48 |
-
# --- Khởi tạo pipeline trên CPU ban đầu ---
|
| 49 |
-
print("Khởi tạo
|
| 50 |
pipeline = create_ltx_video_pipeline(
|
| 51 |
ckpt_path=CFG["checkpoint_path"],
|
| 52 |
precision=CFG["precision"],
|
|
@@ -57,10 +57,10 @@ pipeline = create_ltx_video_pipeline(
|
|
| 57 |
prompt_enhancer_image_caption_model_name_or_path=CFG["prompt_enhancer_image_caption_model_name_or_path"],
|
| 58 |
prompt_enhancer_llm_model_name_or_path=CFG["prompt_enhancer_llm_model_name_or_path"],
|
| 59 |
)
|
| 60 |
-
print("Pipeline sẵn sàng.")
|
| 61 |
print("Khởi tạo latent upsampler trên CPU…")
|
| 62 |
upsampler = create_latent_upsampler(CFG["spatial_upscaler_model_path"], device="cpu")
|
| 63 |
-
print("Latent upsampler sẵn sàng.")
|
| 64 |
|
| 65 |
# --- Các thông số cố định ---
|
| 66 |
FPS = 30.0
|
|
@@ -78,13 +78,12 @@ def calculate_new_dimensions(w, h):
|
|
| 78 |
else:
|
| 79 |
nw = TARGET_SIDE
|
| 80 |
nh = round((nw * h/w)/32)*32
|
| 81 |
-
return (
|
| 82 |
int(max(MIN_DIM, min(nh, MAX_RES))),
|
| 83 |
int(max(MIN_DIM, min(nw, MAX_RES)))
|
| 84 |
)
|
| 85 |
|
| 86 |
def get_duration(*args, **kwargs):
|
| 87 |
-
# spaces.GPU yêu cầu
|
| 88 |
return 75 if kwargs.get("duration_ui",0) > 7 else 60
|
| 89 |
|
| 90 |
@spaces.GPU(duration=get_duration)
|
|
@@ -95,29 +94,29 @@ def generate(prompt, neg_prompt,
|
|
| 95 |
seed, rand_seed, cfg_scale,
|
| 96 |
improve_tex, device_choice,
|
| 97 |
progress=gr.Progress(track_tqdm=True)):
|
| 98 |
-
#
|
| 99 |
dev = "cuda" if device_choice=="GPU" and torch.cuda.is_available() else "cpu"
|
| 100 |
print(f"Chạy trên thiết bị: {dev}")
|
| 101 |
pipeline.to(dev)
|
| 102 |
upsampler.to(dev)
|
| 103 |
|
| 104 |
-
#
|
| 105 |
if rand_seed:
|
| 106 |
seed = random.randint(0, 2**32-1)
|
| 107 |
seed_everething(int(seed))
|
| 108 |
|
| 109 |
-
#
|
| 110 |
tf = max(1, round(duration_ui*FPS))
|
| 111 |
n8 = round((tf-1)/8)
|
| 112 |
n_frames = max(9, min(n8*8+1, MAX_NUM_FRAMES))
|
| 113 |
|
| 114 |
-
#
|
| 115 |
h, w = int(height), int(width)
|
| 116 |
h32 = ((h-1)//32+1)*32
|
| 117 |
w32 = ((w-1)//32+1)*32
|
| 118 |
pad = calculate_padding(h, w, h32, w32)
|
| 119 |
|
| 120 |
-
#
|
| 121 |
kwargs = {
|
| 122 |
"prompt": prompt,
|
| 123 |
"negative_prompt": neg_prompt,
|
|
@@ -136,7 +135,7 @@ def generate(prompt, neg_prompt,
|
|
| 136 |
"offload_to_cpu": False,
|
| 137 |
"enhance_prompt": False,
|
| 138 |
}
|
| 139 |
-
#
|
| 140 |
stg = CFG.get("stg_mode","attention_values").lower()
|
| 141 |
mapping = {
|
| 142 |
"stg_av":SkipLayerStrategy.AttentionValues,
|
|
@@ -150,7 +149,7 @@ def generate(prompt, neg_prompt,
|
|
| 150 |
}
|
| 151 |
kwargs["skip_layer_strategy"] = mapping.get(stg, SkipLayerStrategy.AttentionValues)
|
| 152 |
|
| 153 |
-
#
|
| 154 |
if mode_task=="image-to-video" and img_path:
|
| 155 |
tensor = load_image_to_tensor_with_resize_and_crop(img_path, h, w)
|
| 156 |
tensor = torch.nn.functional.pad(tensor, pad)
|
|
@@ -159,7 +158,7 @@ def generate(prompt, neg_prompt,
|
|
| 159 |
mi = load_media_file(vid_path, h, w, int(frames_to_use), pad).to(dev)
|
| 160 |
kwargs["media_items"] = mi
|
| 161 |
|
| 162 |
-
#
|
| 163 |
if improve_tex:
|
| 164 |
pipe_ms = LTXMultiScalePipeline(pipeline, upsampler)
|
| 165 |
fp = CFG.get("first_pass",{}).copy()
|
|
@@ -187,11 +186,11 @@ def generate(prompt, neg_prompt,
|
|
| 187 |
kwargs.pop(k, None)
|
| 188 |
out = pipeline(**kwargs).images
|
| 189 |
|
| 190 |
-
#
|
| 191 |
-
|
| 192 |
-
sh = None if
|
| 193 |
-
sw = None if
|
| 194 |
-
vid_tensor = out[0][:,:,:n_frames,
|
| 195 |
arr = vid_tensor.permute(1,2,3,0).cpu().numpy()
|
| 196 |
arr = (np.clip(arr,0,1)*255).astype(np.uint8)
|
| 197 |
|
|
@@ -218,56 +217,52 @@ with gr.Blocks(css=css) as demo:
|
|
| 218 |
with gr.Column():
|
| 219 |
# Chọn thiết bị
|
| 220 |
device = gr.Radio(["CPU","GPU"], label="Chạy trên thiết bị", value="CPU")
|
|
|
|
| 221 |
# Tabs
|
| 222 |
with gr.Tab("Ảnh→Video"):
|
| 223 |
-
img_in = gr.Image(label="Ảnh đầu vào", type="filepath",
|
| 224 |
prompt1 = gr.Textbox(label="Mô tả", lines=2, value="Con sinh vật di chuyển")
|
| 225 |
-
btn1
|
|
|
|
| 226 |
with gr.Tab("Văn bản→Video"):
|
| 227 |
prompt2 = gr.Textbox(label="Mô tả", lines=2, value="Rồng bay trên lâu đài")
|
| 228 |
-
btn2
|
|
|
|
| 229 |
with gr.Tab("Video→Video"):
|
| 230 |
-
vid_in = gr.Video(label="Video đầu vào",
|
| 231 |
frames = gr.Slider(label="Số frame dùng", minimum=9, maximum=MAX_NUM_FRAMES, step=8, value=9)
|
| 232 |
prompt3 = gr.Textbox(label="Mô tả", lines=2, value="Chuyển phong cách anime")
|
| 233 |
-
btn3
|
| 234 |
|
| 235 |
duration = gr.Slider(label="Thời lượng (giây)", minimum=0.3, maximum=8.5, step=0.1, value=2)
|
| 236 |
-
improve
|
| 237 |
|
| 238 |
with gr.Column():
|
| 239 |
out_video = gr.Video(label="Kết quả", interactive=False)
|
| 240 |
|
| 241 |
-
#
|
| 242 |
-
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
btn1.click(fn=generate,
|
| 245 |
-
inputs=[prompt1,
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
seed := gr.State(42), gr.State(True),
|
| 250 |
-
cfg_scale := gr.State(CFG["first_pass"]["guidance_scale"]),
|
| 251 |
-
improve, device],
|
| 252 |
-
outputs=[out_video, seed])
|
| 253 |
btn2.click(fn=generate,
|
| 254 |
-
inputs=[prompt2,
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
seed, gr.State(True),
|
| 259 |
-
cfg_scale,
|
| 260 |
-
improve, device],
|
| 261 |
-
outputs=[out_video, seed])
|
| 262 |
btn3.click(fn=generate,
|
| 263 |
-
inputs=[prompt3,
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
seed, gr.State(True),
|
| 268 |
-
cfg_scale,
|
| 269 |
-
improve, device],
|
| 270 |
-
outputs=[out_video, seed])
|
| 271 |
|
| 272 |
if __name__ == "__main__":
|
| 273 |
demo.queue().launch(debug=True, share=False)
|
|
|
|
| 45 |
)
|
| 46 |
CFG["spatial_upscaler_model_path"] = upscaler_path
|
| 47 |
|
| 48 |
+
# --- Khởi tạo pipeline và upsampler trên CPU ban đầu ---
|
| 49 |
+
print("Khởi tạo pipeline trên CPU…")
|
| 50 |
pipeline = create_ltx_video_pipeline(
|
| 51 |
ckpt_path=CFG["checkpoint_path"],
|
| 52 |
precision=CFG["precision"],
|
|
|
|
| 57 |
prompt_enhancer_image_caption_model_name_or_path=CFG["prompt_enhancer_image_caption_model_name_or_path"],
|
| 58 |
prompt_enhancer_llm_model_name_or_path=CFG["prompt_enhancer_llm_model_name_or_path"],
|
| 59 |
)
|
| 60 |
+
print("Pipeline sẵn sàng trên CPU.")
|
| 61 |
print("Khởi tạo latent upsampler trên CPU…")
|
| 62 |
upsampler = create_latent_upsampler(CFG["spatial_upscaler_model_path"], device="cpu")
|
| 63 |
+
print("Latent upsampler sẵn sàng trên CPU.")
|
| 64 |
|
| 65 |
# --- Các thông số cố định ---
|
| 66 |
FPS = 30.0
|
|
|
|
| 78 |
else:
|
| 79 |
nw = TARGET_SIDE
|
| 80 |
nh = round((nw * h/w)/32)*32
|
| 81 |
+
return (
|
| 82 |
int(max(MIN_DIM, min(nh, MAX_RES))),
|
| 83 |
int(max(MIN_DIM, min(nw, MAX_RES)))
|
| 84 |
)
|
| 85 |
|
| 86 |
def get_duration(*args, **kwargs):
|
|
|
|
| 87 |
return 75 if kwargs.get("duration_ui",0) > 7 else 60
|
| 88 |
|
| 89 |
@spaces.GPU(duration=get_duration)
|
|
|
|
| 94 |
seed, rand_seed, cfg_scale,
|
| 95 |
improve_tex, device_choice,
|
| 96 |
progress=gr.Progress(track_tqdm=True)):
|
| 97 |
+
# Chọn thiết bị
|
| 98 |
dev = "cuda" if device_choice=="GPU" and torch.cuda.is_available() else "cpu"
|
| 99 |
print(f"Chạy trên thiết bị: {dev}")
|
| 100 |
pipeline.to(dev)
|
| 101 |
upsampler.to(dev)
|
| 102 |
|
| 103 |
+
# Seed
|
| 104 |
if rand_seed:
|
| 105 |
seed = random.randint(0, 2**32-1)
|
| 106 |
seed_everething(int(seed))
|
| 107 |
|
| 108 |
+
# Tính số frame
|
| 109 |
tf = max(1, round(duration_ui*FPS))
|
| 110 |
n8 = round((tf-1)/8)
|
| 111 |
n_frames = max(9, min(n8*8+1, MAX_NUM_FRAMES))
|
| 112 |
|
| 113 |
+
# Padding kích thước
|
| 114 |
h, w = int(height), int(width)
|
| 115 |
h32 = ((h-1)//32+1)*32
|
| 116 |
w32 = ((w-1)//32+1)*32
|
| 117 |
pad = calculate_padding(h, w, h32, w32)
|
| 118 |
|
| 119 |
+
# Chuẩn bị kwargs
|
| 120 |
kwargs = {
|
| 121 |
"prompt": prompt,
|
| 122 |
"negative_prompt": neg_prompt,
|
|
|
|
| 135 |
"offload_to_cpu": False,
|
| 136 |
"enhance_prompt": False,
|
| 137 |
}
|
| 138 |
+
# Skip-layer strategy
|
| 139 |
stg = CFG.get("stg_mode","attention_values").lower()
|
| 140 |
mapping = {
|
| 141 |
"stg_av":SkipLayerStrategy.AttentionValues,
|
|
|
|
| 149 |
}
|
| 150 |
kwargs["skip_layer_strategy"] = mapping.get(stg, SkipLayerStrategy.AttentionValues)
|
| 151 |
|
| 152 |
+
# Conditioning
|
| 153 |
if mode_task=="image-to-video" and img_path:
|
| 154 |
tensor = load_image_to_tensor_with_resize_and_crop(img_path, h, w)
|
| 155 |
tensor = torch.nn.functional.pad(tensor, pad)
|
|
|
|
| 158 |
mi = load_media_file(vid_path, h, w, int(frames_to_use), pad).to(dev)
|
| 159 |
kwargs["media_items"] = mi
|
| 160 |
|
| 161 |
+
# Multi-scale hay single?
|
| 162 |
if improve_tex:
|
| 163 |
pipe_ms = LTXMultiScalePipeline(pipeline, upsampler)
|
| 164 |
fp = CFG.get("first_pass",{}).copy()
|
|
|
|
| 186 |
kwargs.pop(k, None)
|
| 187 |
out = pipeline(**kwargs).images
|
| 188 |
|
| 189 |
+
# Loại padding, lưu video
|
| 190 |
+
l, r, t, b = pad
|
| 191 |
+
sh = None if b==0 else -b
|
| 192 |
+
sw = None if r==0 else -r
|
| 193 |
+
vid_tensor = out[0][:,:,:n_frames,t:sh,l:sw]
|
| 194 |
arr = vid_tensor.permute(1,2,3,0).cpu().numpy()
|
| 195 |
arr = (np.clip(arr,0,1)*255).astype(np.uint8)
|
| 196 |
|
|
|
|
| 217 |
with gr.Column():
|
| 218 |
# Chọn thiết bị
|
| 219 |
device = gr.Radio(["CPU","GPU"], label="Chạy trên thiết bị", value="CPU")
|
| 220 |
+
|
| 221 |
# Tabs
|
| 222 |
with gr.Tab("Ảnh→Video"):
|
| 223 |
+
img_in = gr.Image(label="Ảnh đầu vào", type="filepath", sources=["upload","clipboard","webcam"])
|
| 224 |
prompt1 = gr.Textbox(label="Mô tả", lines=2, value="Con sinh vật di chuyển")
|
| 225 |
+
btn1 = gr.Button("Tạo từ ảnh")
|
| 226 |
+
|
| 227 |
with gr.Tab("Văn bản→Video"):
|
| 228 |
prompt2 = gr.Textbox(label="Mô tả", lines=2, value="Rồng bay trên lâu đài")
|
| 229 |
+
btn2 = gr.Button("Tạo từ văn bản")
|
| 230 |
+
|
| 231 |
with gr.Tab("Video→Video"):
|
| 232 |
+
vid_in = gr.Video(label="Video đầu vào", type="filepath", sources=["upload","webcam"])
|
| 233 |
frames = gr.Slider(label="Số frame dùng", minimum=9, maximum=MAX_NUM_FRAMES, step=8, value=9)
|
| 234 |
prompt3 = gr.Textbox(label="Mô tả", lines=2, value="Chuyển phong cách anime")
|
| 235 |
+
btn3 = gr.Button("Tạo từ video")
|
| 236 |
|
| 237 |
duration = gr.Slider(label="Thời lượng (giây)", minimum=0.3, maximum=8.5, step=0.1, value=2)
|
| 238 |
+
improve = gr.Checkbox(label="Cải thiện chi tiết", value=True)
|
| 239 |
|
| 240 |
with gr.Column():
|
| 241 |
out_video = gr.Video(label="Kết quả", interactive=False)
|
| 242 |
|
| 243 |
+
# Trạng thái ẩn để điều khiển mode và seed
|
| 244 |
+
mode_state = gr.State("image-to-video")
|
| 245 |
+
seed_state = gr.State(42)
|
| 246 |
+
neg_prompt = gr.State("worst quality, inconsistent motion, blurry, jittery, distorted")
|
| 247 |
+
cfg_scale_st = gr.State(CFG["first_pass"]["guidance_scale"])
|
| 248 |
+
height_st = gr.State(512)
|
| 249 |
+
width_st = gr.State(704)
|
| 250 |
+
|
| 251 |
btn1.click(fn=generate,
|
| 252 |
+
inputs=[prompt1, neg_prompt, img_in, gr.State(""), height_st, width_st,
|
| 253 |
+
mode_state, duration, frames, seed_state, gr.State(True),
|
| 254 |
+
cfg_scale_st, improve, device],
|
| 255 |
+
outputs=[out_video, seed_state])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
btn2.click(fn=generate,
|
| 257 |
+
inputs=[prompt2, neg_prompt, gr.State(""), gr.State(""), height_st, width_st,
|
| 258 |
+
mode_state, duration, frames, seed_state, gr.State(True),
|
| 259 |
+
cfg_scale_st, improve, device],
|
| 260 |
+
outputs=[out_video, seed_state])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 261 |
btn3.click(fn=generate,
|
| 262 |
+
inputs=[prompt3, neg_prompt, gr.State(""), vid_in, height_st, width_st,
|
| 263 |
+
mode_state, duration, frames, seed_state, gr.State(True),
|
| 264 |
+
cfg_scale_st, improve, device],
|
| 265 |
+
outputs=[out_video, seed_state])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
if __name__ == "__main__":
|
| 268 |
demo.queue().launch(debug=True, share=False)
|