Spaces:
Paused
Paused
File size: 8,147 Bytes
1b56622 fb56537 5b259dd 1b56622 67b292e 1b56622 7f72576 ccf9c7e dea92b2 1b56622 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
import gradio as gr
from PIL import Image
import os
import imageio
# Importa a instância do nosso serviço
# O modelo será carregado quando este módulo for importado
from video_service import video_generation_service
# --- FUNÇÕES DE AJUDA PARA A UI (não relacionadas ao modelo) ---
TARGET_FIXED_SIDE = 768
MIN_DIM_SLIDER = 256
MAX_IMAGE_SIZE = 1280
def calculate_new_dimensions(orig_w, orig_h):
if orig_w == 0 or orig_h == 0: return int(TARGET_FIXED_SIDE), int(TARGET_FIXED_SIDE)
if orig_w >= orig_h:
new_h, aspect_ratio = TARGET_FIXED_SIDE, orig_w / orig_h
new_w = round((new_h * aspect_ratio) / 32) * 32
new_w = max(MIN_DIM_SLIDER, min(new_w, MAX_IMAGE_SIZE))
new_h = max(MIN_DIM_SLIDER, min(new_h, MAX_IMAGE_SIZE))
else:
new_w, aspect_ratio = TARGET_FIXED_SIDE, orig_h / orig_w
new_h = round((new_w * aspect_ratio) / 32) * 32
new_h = max(MIN_DIM_SLIDER, min(new_h, MAX_IMAGE_SIZE))
new_w = max(MIN_DIM_SLIDER, min(new_w, MAX_IMAGE_SIZE))
return int(new_h), int(new_w)
def handle_media_upload_for_dims(filepath, current_h, current_w):
if not filepath or not os.path.exists(str(filepath)): return gr.update(value=current_h), gr.update(value=current_w)
try:
if str(filepath).lower().endswith(('.png', '.jpg', '.jpeg', '.webp')):
with Image.open(filepath) as img:
orig_w, orig_h = img.size
else: # Assumir que é um vídeo
with imageio.get_reader(filepath) as reader:
meta = reader.get_meta_data()
orig_w, orig_h = meta.get('size', (current_w, current_h))
new_h, new_w = calculate_new_dimensions(orig_w, orig_h)
return gr.update(value=new_h), gr.update(value=new_w)
except Exception as e:
print(f"Erro ao processar mídia para dimensões: {e}")
return gr.update(value=current_h), gr.update(value=current_w)
# --- FUNÇÃO WRAPPER PARA CHAMAR O SERVIÇO A PARTIR DO GRADIO ---
def gradio_generate_wrapper(prompt, negative_prompt, input_image, input_video,
height, width, mode, duration, frames_to_use,
seed, randomize_seed, guidance_scale, improve_texture,
progress=gr.Progress(track_tqdm=True)):
"""
Esta função atua como uma ponte entre a interface Gradio e o nosso VideoService.
"""
try:
# Define a função de callback para a barra de progresso do Gradio
def progress_handler(step, total_steps):
progress(step / total_steps, desc="Salvando vídeo...")
output_path, used_seed = video_generation_service.generate(
prompt=prompt,
negative_prompt=negative_prompt,
input_image_filepath=input_image,
input_video_filepath=input_video,
height=int(height),
width=int(width),
mode=mode,
duration=float(duration),
frames_to_use=int(frames_to_use),
seed=int(seed),
randomize_seed=bool(randomize_seed),
guidance_scale=float(guidance_scale),
improve_texture=bool(improve_texture),
progress_callback=progress_handler # Passamos o handler para o serviço
)
return output_path, used_seed
except ValueError as e:
# Captura erros de validação do serviço e os exibe na UI
raise gr.Error(str(e))
except Exception as e:
# Captura outros erros inesperados
print(f"Erro inesperado na geração: {e}")
raise gr.Error("Ocorreu um erro inesperado. Verifique os logs.")
# --- DEFINIÇÃO DA INTERFACE GRADIO ---
css = "#col-container { margin: 0 auto; max-width: 900px; }"
with gr.Blocks(css=css) as demo:
gr.Markdown("# LTX Video 0.9.8 13B Distilled")
gr.Markdown("Geração de vídeo rápida e de alta qualidade.")
with gr.Row():
with gr.Column():
# ... (Layout das abas e componentes exatamente como antes) ...
with gr.Tab("image-to-video") as image_tab:
video_i_hidden = gr.Textbox(label="video_i", visible=False, value=None)
image_i2v = gr.Image(label="Input Image", type="filepath", sources=["upload", "webcam", "clipboard"])
i2v_prompt = gr.Textbox(label="Prompt", value="The creature from the image starts to move", lines=3)
i2v_button = gr.Button("Generate Image-to-Video", variant="primary")
with gr.Tab("text-to-video") as text_tab:
image_n_hidden = gr.Textbox(label="image_n", visible=False, value=None)
video_n_hidden = gr.Textbox(label="video_n", visible=False, value=None)
t2v_prompt = gr.Textbox(label="Prompt", value="A majestic dragon flying over a medieval castle", lines=3)
t2v_button = gr.Button("Generate Text-to-Video", variant="primary")
with gr.Tab("video-to-video", visible=True) as video_tab:
image_v_hidden = gr.Textbox(label="image_v", visible=False, value=None)
video_v2v = gr.Video(label="Input Video", sources=["upload", "webcam"])
frames_to_use = gr.Slider(label="Frames to use from input video", minimum=9, maximum=257, value=9, step=8, info="Must be N*8+1.")
v2v_prompt = gr.Textbox(label="Prompt", value="Change the style to cinematic anime", lines=3)
v2v_button = gr.Button("Generate Video-to-Video", variant="primary")
duration_input = gr.Slider(label="Video Duration (seconds)", minimum=0.3, maximum=8.5, value=2, step=0.1)
improve_texture = gr.Checkbox(label="Improve Texture (multi-scale)", value=True, visible=True)
with gr.Column():
output_video = gr.Video(label="Generated Video", interactive=False)
with gr.Accordion("Advanced settings", open=False):
mode = gr.Dropdown(["text-to-video", "image-to-video", "video-to-video"], label="task", value="image-to-video", visible=False)
negative_prompt_input = gr.Textbox(label="Negative Prompt", value="worst quality, inconsistent motion, blurry, jittery, distorted", lines=2)
with gr.Row():
seed_input = gr.Number(label="Seed", value=42, precision=0)
randomize_seed_input = gr.Checkbox(label="Randomize Seed", value=True)
guidance_scale_input = gr.Slider(label="Guidance Scale (CFG)", minimum=1.0, maximum=10.0, value=3.0, step=0.1)
with gr.Row():
height_input = gr.Slider(label="Height", value=512, step=32, minimum=MIN_DIM_SLIDER, maximum=MAX_IMAGE_SIZE)
width_input = gr.Slider(label="Width", value=704, step=32, minimum=MIN_DIM_SLIDER, maximum=MAX_IMAGE_SIZE)
# --- LÓGICA DE EVENTOS DA UI ---
image_i2v.upload(fn=handle_media_upload_for_dims, inputs=[image_i2v, height_input, width_input], outputs=[height_input, width_input])
video_v2v.upload(fn=handle_media_upload_for_dims, inputs=[video_v2v, height_input, width_input], outputs=[height_input, width_input])
image_tab.select(fn=lambda: "image-to-video", outputs=[mode])
text_tab.select(fn=lambda: "text-to-video", outputs=[mode])
video_tab.select(fn=lambda: "video-to-video", outputs=[mode])
common_inputs = [negative_prompt_input, height_input, width_input, mode, duration_input, frames_to_use, seed_input, randomize_seed_input, guidance_scale_input, improve_texture]
common_outputs = [output_video, seed_input]
t2v_button.click(fn=gradio_generate_wrapper, inputs=[t2v_prompt, *common_inputs[:1], image_n_hidden, video_n_hidden, *common_inputs[1:]], outputs=common_outputs, api_name="text_to_video")
i2v_button.click(fn=gradio_generate_wrapper, inputs=[i2v_prompt, *common_inputs[:1], image_i2v, video_i_hidden, *common_inputs[1:]], outputs=common_outputs, api_name="image_to_video")
v2v_button.click(fn=gradio_generate_wrapper, inputs=[v2v_prompt, *common_inputs[:1], image_v_hidden, video_v2v, *common_inputs[1:]], outputs=common_outputs, api_name="video_to_video")
if __name__ == "__main__":
demo.queue().launch(debug=True, share=False)
|