6666)
6767
6868
69+ def _prepare_video_batch (
70+ images : torch .Tensor ,
71+ start_idx : int ,
72+ end_idx : int ,
73+ uniform_padding : int = 0 ,
74+ debug : Optional ['Debug' ] = None ,
75+ log_info : bool = False
76+ ) -> torch .Tensor :
77+ """
78+ Extract and prepare video batch with uniform padding and permutation.
79+
80+ Args:
81+ images: Source video frames [T, H, W, C]
82+ start_idx: Start frame index
83+ end_idx: End frame index (exclusive)
84+ uniform_padding: Number of frames to pad (0 = no padding)
85+ debug: Debug instance for optional logging
86+ log_info: If True, log padding operations (used during encoding only)
87+
88+ Returns:
89+ Prepared video in TCHW format
90+ """
91+ # Extract frames (view/slice, not copy)
92+ video = images [start_idx :end_idx ]
93+
94+ # Apply uniform padding if needed
95+ if uniform_padding > 0 :
96+ if log_info and debug :
97+ current_frames = end_idx - start_idx
98+ debug .log (f"Sequence of { current_frames } frames" , category = "video" , force = True , indent_level = 1 )
99+ debug .log (f"Padding batch: { uniform_padding } frame{ 's' if uniform_padding != 1 else '' } added ({ current_frames } → { current_frames + uniform_padding } ) for uniform batches" ,
100+ category = "video" , force = True , indent_level = 1 )
101+ video = pad_video_temporal (video , count = uniform_padding , temporal_dim = 0 , prepend = False , debug = None )
102+
103+ # Permute to TCHW format
104+ video = video .permute (0 , 3 , 1 , 2 )
105+
106+ return video
107+
108+
109+ def _apply_4n1_padding (video : torch .Tensor ) -> torch .Tensor :
110+ """
111+ Apply 4n+1 temporal padding constraint required by VAE.
112+
113+ Args:
114+ video: Video tensor in TCHW format
115+
116+ Returns:
117+ Padded video in TCHW format
118+ """
119+ t = video .size (0 )
120+ if t % 4 != 1 :
121+ video = optimized_single_video_rearrange (video ) # TCHW -> CTHW
122+ video = pad_video_temporal (video , temporal_dim = 1 , prepend = False , debug = None )
123+ video = optimized_single_video_rearrange (video ) # CTHW -> TCHW
124+ return video
125+
126+
127+ def _reconstruct_and_transform_batch (
128+ ctx : Dict [str , Any ],
129+ batch_idx : int ,
130+ debug : Optional ['Debug' ] = None
131+ ) -> torch .Tensor :
132+ """
133+ Reconstruct and transform a video batch for color correction (Phase 4).
134+
135+ Args:
136+ ctx: Context with input_images, batch_metadata, video_transform
137+ batch_idx: Index of batch to reconstruct
138+ debug: Debug instance for logging
139+
140+ Returns:
141+ Transformed video in CTHW format, ready for color correction
142+ """
143+ start_idx , end_idx , uniform_padding = ctx ['batch_metadata' ][batch_idx ]
144+
145+ # Prepare video batch
146+ video = _prepare_video_batch (
147+ images = ctx ['input_images' ],
148+ start_idx = start_idx ,
149+ end_idx = end_idx ,
150+ uniform_padding = uniform_padding ,
151+ debug = None ,
152+ log_info = False
153+ )
154+
155+ # Apply 4n+1 padding using shared helper
156+ video = _apply_4n1_padding (video )
157+
158+ # Extract RGB and transform
159+ if ctx .get ('is_rgba' , False ):
160+ rgb_video = video [:, :3 , :, :]
161+ else :
162+ rgb_video = video
163+
164+ transformed_video = ctx ['video_transform' ](rgb_video )
165+
166+ del video
167+
168+ return transformed_video
169+
170+
69171def encode_all_batches (
70172 runner : 'VideoDiffusionInfer' ,
71173 ctx : Dict [str , Any ],
@@ -106,7 +208,7 @@ def encode_all_batches(
106208
107209 Returns:
108210 dict: Context containing:
109- - all_transformed_videos: List of (video, original_length) tuples
211+ - batch_metadata: Lightweight indices for on-demand transform reconstruction
110212 - all_latents: List of encoded latents ready for upscaling
111213 - Other state for subsequent phases
112214
@@ -179,9 +281,7 @@ def encode_all_batches(
179281 ctx ['all_latents' ] = [None ] * num_encode_batches
180282 ctx ['all_ori_lengths' ] = [None ] * num_encode_batches
181283 if color_correction != "none" :
182- ctx ['all_transformed_videos' ] = [None ] * num_encode_batches
183- else :
184- ctx ['all_transformed_videos' ] = None
284+ ctx ['batch_metadata' ] = [None ] * num_encode_batches
185285
186286 encode_idx = 0
187287
@@ -246,23 +346,21 @@ def encode_all_batches(
246346 debug .log (f"Encoding batch { encode_idx + 1 } /{ num_encode_batches } " , category = "vae" , force = True )
247347 debug .start_timer (f"encode_batch_{ encode_idx + 1 } " )
248348
249- # Save original length BEFORE any padding (critical for post-processing trimming)
349+ # Save original length before any padding
250350 ori_length = current_frames
251351
252- # Process current batch
253- video = images [start_idx :end_idx ]
254-
255- # Log uniform padding if applied
352+ # Prepare video batch with uniform padding
353+ video = _prepare_video_batch (
354+ images = images ,
355+ start_idx = start_idx ,
356+ end_idx = end_idx ,
357+ uniform_padding = batch_size - current_frames if is_uniform_padding else 0 ,
358+ debug = debug ,
359+ log_info = True
360+ )
256361 if is_uniform_padding :
257- padding_for_uniform = batch_size - current_frames
258- debug .log (f"Sequence of { current_frames } frames" , category = "video" , force = True , indent_level = 1 )
259- debug .log (f"Padding batch: { padding_for_uniform } frame{ 's' if padding_for_uniform != 1 else '' } added ({ current_frames } → { batch_size } ) for uniform batches" ,
260- category = "video" , force = True , indent_level = 1 )
261- video = pad_video_temporal (video , count = padding_for_uniform , temporal_dim = 0 , prepend = False , debug = None )
262362 current_frames = batch_size
263363
264- # Permute and move to device
265- video = video .permute (0 , 3 , 1 , 2 )
266364 video = manage_tensor (
267365 tensor = video ,
268366 target_device = ctx ['vae_device' ],
@@ -280,29 +378,23 @@ def encode_all_batches(
280378 if not is_uniform_padding :
281379 debug .log (f"Sequence of { t } frames" , category = "video" , force = True , indent_level = 1 )
282380
283- # Apply 4n+1 padding if needed
381+ # Apply 4n+1 padding using shared helper
284382 if t % 4 != 1 :
285383 target = ((t - 1 )// 4 + 1 )* 4 + 1
286384 padding_frames = target - t
287385 debug .log (f"Padding batch: { padding_frames } frame{ 's' if padding_frames != 1 else '' } added ({ t } → { target } ) to meet 4n+1 constraint" ,
288386 category = "video" , force = True , indent_level = 1 )
289-
290- # Pad video using reversed frames (TCHW format, need to convert to CTHW)
291- video = optimized_single_video_rearrange (video ) # TCHW -> CTHW
292- video = pad_video_temporal (video , temporal_dim = 1 , prepend = False , debug = None )
293- video = optimized_single_video_rearrange (video ) # CTHW -> TCHW
387+ # Apply 4n+1 padding to match exact frame count from encoding
388+ video = _apply_4n1_padding (video )
294389
295- # Extract RGB for transforms (view, not copy )
390+ # Apply transformations (matches reconstruction logic )
296391 if ctx .get ('is_rgba' , False ):
297- rgb_for_transform = video [:, :3 , :, :]
298392 debug .log (f"Extracted Alpha channel for edge-guided upscaling" , category = "alpha" , indent_level = 1 )
393+ rgb_video = video [:, :3 , :, :]
299394 else :
300- rgb_for_transform = video
301-
302- # Apply transformations (to RGB from already-padded video)
303- transformed_video = ctx ['video_transform' ](rgb_for_transform )
395+ rgb_video = video
304396
305- del rgb_for_transform
397+ transformed_video = ctx [ 'video_transform' ]( rgb_video )
306398
307399 # Apply input noise if requested (to reduce artifacts at high resolutions)
308400 if input_noise_scale > 0 :
@@ -325,6 +417,10 @@ def encode_all_batches(
325417 # Store original length for proper trimming later
326418 ctx ['all_ori_lengths' ][encode_idx ] = ori_length
327419
420+ # Store batch frame indices for on-demand reconstruction
421+ if color_correction != "none" :
422+ ctx ['batch_metadata' ][encode_idx ] = (start_idx , end_idx , batch_size - ori_length if is_uniform_padding else 0 )
423+
328424 # Extract and store Alpha and RGB from padded original video (before encoding)
329425 if ctx .get ('is_rgba' , False ):
330426 if 'all_alpha_channels' not in ctx :
@@ -375,26 +471,9 @@ def encode_all_batches(
375471
376472 # Encode to latents
377473 cond_latents = runner .vae_encode ([transformed_video ])
378-
379- # Store transformed video for color correction after encoding
380- if color_correction != "none" :
381- if ctx ['tensor_offload_device' ] is not None :
382- # Move to offload device to free VRAM
383- ctx ['all_transformed_videos' ][encode_idx ] = manage_tensor (
384- tensor = transformed_video ,
385- target_device = ctx ['tensor_offload_device' ],
386- tensor_name = f"transformed_video_{ encode_idx + 1 } " ,
387- debug = debug ,
388- reason = "storing input reference for color correction" ,
389- indent_level = 1
390- )
391- else :
392- # No offload device - keep reference on VAE device
393- ctx ['all_transformed_videos' ][encode_idx ] = transformed_video
394-
395- # Clean up transformed_video reference if not needed or already offloaded
396- if color_correction == "none" or ctx ['tensor_offload_device' ] is not None :
397- del transformed_video
474+
475+ # Don't store transformed_video - will reconstruct on-demand in Phase 4
476+ del transformed_video , rgb_video
398477
399478 # Convert from VAE dtype to compute dtype and offload to avoid VRAM accumulation
400479 if ctx ['tensor_offload_device' ] is not None and (cond_latents [0 ].is_cuda or cond_latents [0 ].is_mps ):
@@ -1031,13 +1110,14 @@ def postprocess_all_batches(
10311110 video_idx = min (batch_idx , len (ctx ['all_ori_lengths' ]) - 1 )
10321111 ori_length = ctx ['all_ori_lengths' ][video_idx ] if 'all_ori_lengths' in ctx else sample .shape [0 ]
10331112
1034- # Retrieve transformed video early for consistent trimming
1113+ # Reconstruct transformed video on-demand for color correction
10351114 input_video = None
1036- if color_correction != "none" and ctx .get ('all_transformed_videos ' ) is not None :
1037- if video_idx < len (ctx ['all_transformed_videos ' ]) and ctx ['all_transformed_videos ' ][video_idx ] is not None :
1038- transformed_video = ctx [ 'all_transformed_videos' ][ video_idx ]
1039- # Convert transformed video from C T H W to T C H W format
1115+ if color_correction != "none" and ctx .get ('batch_metadata ' ) is not None :
1116+ if video_idx < len (ctx ['batch_metadata ' ]) and ctx ['batch_metadata ' ][video_idx ] is not None :
1117+ # Reconstruct transformation
1118+ transformed_video = _reconstruct_and_transform_batch ( ctx , video_idx , debug )
10401119 input_video = optimized_single_video_rearrange (transformed_video )
1120+ del transformed_video
10411121
10421122 # Trim both sample and input_video to original length if necessary (handles temporal padding)
10431123 if ori_length < sample .shape [0 ]:
@@ -1112,9 +1192,8 @@ def postprocess_all_batches(
11121192
11131193 debug .end_timer (f"color_correction_{ color_correction } " , f"Color correction ({ color_correction } )" )
11141194
1115- # Free the transformed video
1116- ctx ['all_transformed_videos' ][video_idx ] = None
1117- del input_video , transformed_video
1195+ # Free the reconstructed transformed video
1196+ del input_video
11181197
11191198 # Recombine with Alpha if it was present in input
11201199 if has_alpha and alpha_channel is not None :
@@ -1299,8 +1378,7 @@ def postprocess_all_batches(
12991378 del ctx ['video_transform' ]
13001379
13011380 # 3. Clean up storage lists (all_latents, all_alpha_channels, etc.)
1302- tensor_storage_keys = ['all_latents' , 'all_transformed_videos' ,
1303- 'all_alpha_channels' , 'all_input_rgb' ]
1381+ tensor_storage_keys = ['all_latents' , 'all_alpha_channels' , 'all_input_rgb' ]
13041382 for key in tensor_storage_keys :
13051383 if key in ctx and ctx [key ]:
13061384 release_tensor_collection (ctx [key ])
@@ -1311,6 +1389,11 @@ def postprocess_all_batches(
13111389 del ctx ['all_ori_lengths' ]
13121390 if 'true_target_dims' in ctx :
13131391 del ctx ['true_target_dims' ]
1392+ if 'batch_metadata' in ctx :
1393+ del ctx ['batch_metadata' ]
1394+ if 'input_images' in ctx :
1395+ release_tensor_memory (ctx ['input_images' ])
1396+ del ctx ['input_images' ]
13141397
13151398 debug .end_timer ("phase4_postprocessing" , "Phase 4: Post-processing complete" , show_breakdown = True )
13161399 debug .log_memory_state ("After phase 4 (Post-processing)" , show_tensors = False )
0 commit comments