Skip to content

Commit 79f3090

Browse files
feat(ltx): wire last-frame conditioning into LTX tab
The LTX tab already collected a last-frame image but anchored every condition at index 0, so it never acted as a last frame. Build a separate condition for it at the final frame: index -1 for the 2.x family (latent index, negatives wrap) and num_frames-1 for 0.9 (pixel index). The Last image input now shows only for Condition models, the pipelines that accept multi-frame conditioning.
1 parent b7f24f5 commit 79f3090

3 files changed

Lines changed: 23 additions & 10 deletions

File tree

modules/ltx/ltx_process.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,6 @@ def abort(e, ok: bool = False, p=None):
234234
condition_images = []
235235
if ltx_init_image is not None:
236236
condition_images.append(ltx_init_image)
237-
if condition_last is not None:
238-
condition_images.append(condition_last)
239237
conditions = []
240238
conditions_stage2 = []
241239
if caps.supports_multi_condition:
@@ -246,14 +244,14 @@ def abort(e, ok: bool = False, p=None):
246244
base_w, base_h, condition_strength,
247245
condition_images, condition_files, condition_video,
248246
condition_video_frames, condition_video_skip,
249-
family=caps.family,
247+
family=caps.family, num_frames=get_frames(frames), condition_last=condition_last,
250248
)
251249
if (final_w, final_h) != (base_w, base_h):
252250
conditions_stage2 = get_conditions(
253251
final_w, final_h, condition_strength,
254252
condition_images, condition_files, condition_video,
255253
condition_video_frames, condition_video_skip,
256-
family=caps.family,
254+
family=caps.family, num_frames=get_frames(frames), condition_last=condition_last,
257255
)
258256
else:
259257
conditions_stage2 = conditions

modules/ltx/ltx_ui.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ def _model_change(model_name: str):
1616
return (
1717
gr.update(visible=False), # input_media_accordion
1818
gr.update(visible=False), # multi_condition_group
19+
gr.update(visible=False), # last_image
1920
gr.update(visible=False), # upsample_accordion
2021
gr.update(visible=False), # refine_accordion
2122
gr.update(value=False), # upsample_enable (reset)
@@ -38,6 +39,7 @@ def _model_change(model_name: str):
3839
return (
3940
gr.update(visible=caps.supports_input_media),
4041
gr.update(visible=caps.supports_multi_condition),
42+
gr.update(visible=caps.supports_multi_condition), # last_image
4143
gr.update(visible=True),
4244
gr.update(visible=True),
4345
gr.update(value=False),
@@ -73,7 +75,7 @@ def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4
7375
ltx_init_image = gr.Image(label='Image', elem_id='ltx_init_image', type='pil', image_mode='RGB', width=256, height=256)
7476
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')
7577
with gr.Row():
76-
last_image = gr.Image(label='Last image', elem_id='ltx_last_image', type='pil', image_mode='RGB', width=256, height=256)
78+
last_image = gr.Image(label='Last image', elem_id='ltx_last_image', type='pil', image_mode='RGB', width=256, height=256, visible=False)
7779
multi_condition_group = gr.Group(visible=False)
7880
with multi_condition_group:
7981
gr.Markdown('**Prefix conditioning**: supply a video or gallery to anchor the opening frames', elem_id='ltx_prefix_conditioning_label')
@@ -123,6 +125,7 @@ def create_ui(prompt, negative, styles, overrides, mp4_fps, mp4_interpolate, mp4
123125
outputs=[
124126
input_media_accordion,
125127
multi_condition_group,
128+
last_image,
126129
upsample_accordion,
127130
refine_accordion,
128131
upsample_enable,

modules/ltx/ltx_util.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,15 +138,15 @@ def _condition_cls(family: str):
138138
return LTXVideoCondition
139139

140140

141-
def make_condition(condition_cls, family: str, frames, strength: float, is_video: bool):
141+
def make_condition(condition_cls, family: str, frames, strength: float, is_video: bool, index: int = 0):
142142
if family == '2.x':
143-
return condition_cls(frames=frames, index=0, strength=strength)
143+
return condition_cls(frames=frames, index=index, strength=strength)
144144
if is_video:
145-
return condition_cls(video=frames, frame_index=0, strength=strength)
146-
return condition_cls(image=frames, frame_index=0, strength=strength)
145+
return condition_cls(video=frames, frame_index=index, strength=strength)
146+
return condition_cls(image=frames, frame_index=index, strength=strength)
147147

148148

149-
def get_conditions(width, height, condition_strength, condition_images, condition_files, condition_video, condition_video_frames, condition_video_skip, family: str = '0.9'):
149+
def get_conditions(width, height, condition_strength, condition_images, condition_files, condition_video, condition_video_frames, condition_video_skip, family: str = '0.9', num_frames=None, condition_last=None):
150150
condition_cls = _condition_cls(family)
151151
if condition_cls is None:
152152
return []
@@ -186,6 +186,18 @@ def get_conditions(width, height, condition_strength, condition_images, conditio
186186
log.debug(f'Video condition: family={family} frames={len(condition_frames)} size={condition_frames[0].size} strength={condition_strength}')
187187
except Exception as e:
188188
log.error(f'LTX condition video: {e}')
189+
if condition_last is not None:
190+
try:
191+
if isinstance(condition_last, str):
192+
from modules.api.api import decode_base64_to_image
193+
condition_last = decode_base64_to_image(condition_last)
194+
condition_last = condition_last.convert('RGB').resize((width, height), resample=Image.Resampling.LANCZOS)
195+
# 2.x reads index as a latent index and accepts -1 for the final frame; 0.9 uses a pixel index.
196+
last_index = -1 if family == '2.x' else max((num_frames or 1) - 1, 0)
197+
conditions.append(make_condition(condition_cls, family, condition_last, condition_strength, is_video=False, index=last_index))
198+
log.debug(f'Video condition: family={family} last={condition_last.size} index={last_index} strength={condition_strength}')
199+
except Exception as e:
200+
log.error(f'LTX condition last image: {e}')
189201
return conditions
190202

191203

0 commit comments

Comments
 (0)