mirror of
https://github.com/vladmandic/automatic
synced 2026-08-29 00:20:59 +02:00
acac6157b0
Rework the LTX Video tab so one UI handles every registered variant (0.9.0 through 2.3, Dev/Distilled/SDNQ-4Bit, T2V/I2V/Condition). Per- variant behavior is driven from a single capability lookup rather than substring matching on model names scattered across the backend. - modules/ltx/ltx_capabilities.py: new module computing family, is_i2v, distilled, supports_input_media, supports_multi_condition, supports_image_cond_noise_scale, supports_decode_timestep, supports_stg, supports_audio, supports_frame_rate_kwarg, and the default CFG / steps / sampler_shift for a given model name by reading its registered repo_cls in models_def. - modules/ltx/ltx_ui.py: capability-gated UI. Selecting a model rewires accordion visibility, slider interactivity, and defaults via a single model.change handler. New controls: dedicated image input slot inside the LTX tab (replaces the disconnected shared init_image for I2V), condition strength slider, CFG / sampler shift / dynamic shift sliders that were previously unreachable. Input media accordion restructured so the image slot is always-visible while the video / gallery prefix tabs only appear on Condition pipelines. - modules/ltx/ltx_process.py: route the base pass through processing.process_images(p) so LTX inherits standard scheduler wiring, extra_networks activation, VAE handling, and error plumbing from StableDiffusionProcessingVideo. The multi-pass latent path (upsample / refine) stays on direct pipeline calls for latent re-entry. Refine noise control gets family-specific kwargs: denoise_strength for 0.9.x LTXConditionPipeline, noise_scale for all 2.x pipelines; the prior strength= injection crashed on 2.x and only affected conditioning intensity on 0.9.x. Add torch_gc between every stage boundary (base to upsample to refine to vae decode) so the CUDA allocator cache does not retain the prior pass's allocations across stages. Remove the TypeError fallback that silently passed raw latents to save_video when VAE decode returned None on OOM; those errors now surface cleanly. - modules/ltx/ltx_util.py: get_conditions grows a family parameter and builds LTX2VideoCondition (frames, index, strength) for 2.x or LTXVideoCondition (image, video, frame_index, strength) for 0.9.x. get_bucket floors to max(32, vae_spatial_compression_ratio) since LTX pipelines validate divisibility by 32 regardless of family. - modules/video_models/video_overrides.py: extend the I2V generator reset to cover LTX2ImageToVideoPipeline and both Condition classes. Keep the strength= kwarg injection gated to 0.9.x LTXConditionPipeline only; LTX2ConditionPipeline.__call__ does not accept it (per-condition strength lives on the LTX2VideoCondition dataclass instead).
156 lines
8.4 KiB
Python
156 lines
8.4 KiB
Python
import os
|
|
import gradio as gr
|
|
from modules import ui_sections
|
|
from modules.logger import log
|
|
from modules.video_models.models_def import models
|
|
from modules.ltx import ltx_process, ltx_capabilities
|
|
|
|
|
|
debug = log.trace if os.environ.get('SD_VIDEO_DEBUG', None) is not None else lambda *args, **kwargs: None
|
|
|
|
|
|
def _model_change(model_name: str):
|
|
caps = ltx_capabilities.get_caps(model_name)
|
|
if caps is None:
|
|
return (
|
|
gr.update(visible=False), # input_media_accordion
|
|
gr.update(visible=False), # multi_condition_group
|
|
gr.update(visible=False), # upsample_accordion
|
|
gr.update(visible=False), # refine_accordion
|
|
gr.update(value=False), # upsample_enable (reset)
|
|
gr.update(value=False), # refine_enable (reset)
|
|
gr.update(interactive=True), # refine_strength
|
|
gr.update(), # guidance_scale
|
|
gr.update(), # steps
|
|
gr.update(), # sampler_shift
|
|
gr.update(interactive=False), # decode_timestep
|
|
gr.update(interactive=False), # image_cond_noise_scale
|
|
gr.update(visible=False), # audio_accordion
|
|
)
|
|
# distilled 2.x variants use a fixed canonical refine schedule; the strength slider is meaningless there
|
|
refine_strength_interactive = not (caps.family == '2.x' and caps.is_distilled)
|
|
return (
|
|
gr.update(visible=caps.supports_input_media),
|
|
gr.update(visible=caps.supports_multi_condition),
|
|
gr.update(visible=True),
|
|
gr.update(visible=True),
|
|
gr.update(value=False),
|
|
gr.update(value=False),
|
|
gr.update(interactive=refine_strength_interactive),
|
|
gr.update(value=caps.default_cfg),
|
|
gr.update(value=caps.default_steps),
|
|
gr.update(value=caps.default_sampler_shift),
|
|
gr.update(interactive=caps.supports_decode_timestep),
|
|
gr.update(interactive=caps.supports_image_cond_noise_scale),
|
|
gr.update(visible=caps.supports_audio),
|
|
)
|
|
|
|
|
|
def create_ui(prompt, negative, styles, overrides, init_image, _init_strength, last_image, mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf, width, height, frames, seed):
|
|
with gr.Row():
|
|
with gr.Column(variant='compact', elem_id="ltx_settings", elem_classes=['settings-column'], scale=1):
|
|
with gr.Row():
|
|
generate = gr.Button('Generate', elem_id="ltx_generate_btn", variant='primary', visible=False)
|
|
with gr.Row():
|
|
ltx_models = [m.name for m in models['LTX Video']] if 'LTX Video' in models else ['None']
|
|
model = gr.Dropdown(label='LTX model', choices=ltx_models, value=ltx_models[0], elem_id="ltx_model")
|
|
input_media_accordion = gr.Accordion(open=False, label="Input media", elem_id='ltx_input_media_accordion', visible=False)
|
|
with input_media_accordion:
|
|
ltx_init_image = gr.Image(label='Image', elem_id='ltx_init_image', type='pil', image_mode='RGB', width=256, height=256)
|
|
ltx_condition_strength = gr.Slider(label='LTX input strength', minimum=0.0, maximum=1.0, step=0.05, value=1.0, elem_id='ltx_condition_strength')
|
|
multi_condition_group = gr.Group(visible=False)
|
|
with multi_condition_group:
|
|
gr.Markdown('**Prefix conditioning**: supply a video or gallery to anchor the opening frames', elem_id='ltx_prefix_conditioning_label')
|
|
with gr.Tabs():
|
|
with gr.Tab('Video prefix', id='ltx_condition_video_tab'):
|
|
condition_video = gr.Video(label='Video', type='filepath', elem_id="ltx_condition_video", width=256, height=256, source='upload')
|
|
with gr.Row():
|
|
condition_video_frames = gr.Slider(label='LTX frames number', minimum=-1, maximum=1024, step=1, value=-1, elem_id="ltx_condition_video_frames")
|
|
condition_video_skip = gr.Slider(label='LTX frames skip', minimum=0, maximum=1024, step=1, value=0, elem_id="ltx_condition_video_sip")
|
|
with gr.Tab('Gallery prefix', id='ltx_condition_batch_tab'):
|
|
condition_files = gr.Files(label="Image Batch", interactive=True, elem_id="ltx_condition_batch")
|
|
upsample_accordion = gr.Accordion(open=False, label="Upsample", elem_id='ltx_upsample_accordion')
|
|
with upsample_accordion:
|
|
with gr.Row():
|
|
upsample_enable = gr.Checkbox(label='LTX enable upsampling', value=False, elem_id="ltx_upsample_enable")
|
|
upsample_ratio = gr.Slider(label='LTX upsample ratio', minimum=1.0, maximum=4.0, step=0.1, value=2.0, elem_id="ltx_upsample_ratio")
|
|
refine_accordion = gr.Accordion(open=False, label="Refine", elem_id='ltx_refine_accordion')
|
|
with refine_accordion:
|
|
with gr.Row():
|
|
refine_enable = gr.Checkbox(label='LTX enable refine', value=False, elem_id="ltx_refine_enable")
|
|
refine_strength = gr.Slider(label='LTX refine strength', minimum=0.1, maximum=1.0, step=0.05, value=0.4, elem_id="ltx_refine_strength")
|
|
parameters_accordion = gr.Accordion(open=False, label="Advanced", elem_id='ltx_parameters_accordion')
|
|
with parameters_accordion:
|
|
steps, sampler_index = ui_sections.create_sampler_and_steps_selection(None, "ltx", default_steps=40)
|
|
with gr.Row():
|
|
guidance_scale = gr.Slider(label='LTX guidance scale', minimum=0.0, maximum=14.0, step=0.1, value=4.0, elem_id="ltx_guidance_scale")
|
|
with gr.Row():
|
|
sampler_shift = gr.Slider(label='LTX sampler shift', minimum=-1.0, maximum=20.0, step=0.1, value=-1.0, elem_id="ltx_sampler_shift")
|
|
dynamic_shift = gr.Checkbox(label='LTX dynamic shift', value=False, elem_id="ltx_dynamic_shift")
|
|
with gr.Row():
|
|
decode_timestep = gr.Slider(label='LTX decode timestep', minimum=0.0, maximum=1.0, step=0.01, value=0.05, elem_id="ltx_decode_timestep")
|
|
image_cond_noise_scale = gr.Slider(label='LTX image cond noise scale', minimum=0.0, maximum=1.0, step=0.005, value=0.025, elem_id="ltx_image_cond_noise_scale")
|
|
audio_accordion = gr.Accordion(open=False, label="Audio", elem_id='ltx_audio_accordion', visible=False)
|
|
with audio_accordion:
|
|
with gr.Row():
|
|
audio_enable = gr.Checkbox(label='LTX enable audio', value=False, elem_id="ltx_audio_enable")
|
|
|
|
with gr.Column(elem_id='ltx-output-column', scale=2) as _column_output:
|
|
with gr.Row():
|
|
video = gr.Video(label="Output", show_label=False, elem_id='ltx_output_video', elem_classes=['control-image'], height=512, autoplay=False)
|
|
with gr.Row():
|
|
text = gr.HTML('', elem_id='ltx_generation_info', show_label=False)
|
|
|
|
model.change(
|
|
fn=_model_change,
|
|
inputs=[model],
|
|
outputs=[
|
|
input_media_accordion,
|
|
multi_condition_group,
|
|
upsample_accordion,
|
|
refine_accordion,
|
|
upsample_enable,
|
|
refine_enable,
|
|
refine_strength,
|
|
guidance_scale,
|
|
steps,
|
|
sampler_shift,
|
|
decode_timestep,
|
|
image_cond_noise_scale,
|
|
audio_accordion,
|
|
],
|
|
)
|
|
|
|
task_id = gr.Textbox(visible=False, value='')
|
|
ui_state = gr.Textbox(visible=False, value='')
|
|
state_inputs = [task_id, ui_state]
|
|
|
|
video_inputs = [
|
|
model,
|
|
prompt, negative, styles,
|
|
width, height, frames,
|
|
steps, sampler_index,
|
|
guidance_scale, sampler_shift, dynamic_shift,
|
|
seed,
|
|
upsample_enable, upsample_ratio,
|
|
refine_enable, refine_strength,
|
|
ltx_condition_strength, ltx_init_image, init_image, last_image, condition_files, condition_video, condition_video_frames, condition_video_skip,
|
|
decode_timestep, image_cond_noise_scale,
|
|
mp4_fps, mp4_interpolate, mp4_codec, mp4_ext, mp4_opt, mp4_video, mp4_frames, mp4_sf,
|
|
audio_enable,
|
|
overrides,
|
|
]
|
|
video_outputs = [
|
|
video,
|
|
text,
|
|
]
|
|
|
|
video_dict = dict(
|
|
fn=ltx_process.run_ltx,
|
|
_js="submit_ltx",
|
|
inputs=state_inputs + video_inputs,
|
|
outputs=video_outputs,
|
|
show_progress='hidden',
|
|
)
|
|
generate.click(**video_dict)
|