@@ -129,13 +129,75 @@ class Qwen3OmniPreprocessorOutput(mm_utils.PreprocessorOutput):
129129 video_values : None | np .ndarray = None
130130 video_grid_thw : None | np .ndarray = None
131131 video_second_per_grid : None | np .ndarray = None
132+ video_mask : None | np .ndarray = None
132133 # Audio attributes.
133134 num_audios : int = 0
134135 audio_values : None | np .ndarray = None
135136 audio_mask : None | np .ndarray = None
136137 audio_lengths : None | np .ndarray = None
137138
138139
140+ def maybe_pad_video_values_to_max_grid (
141+ video_values : np .ndarray ,
142+ video_grid_thw : np .ndarray ,
143+ config ,
144+ ) -> tuple [np .ndarray , np .ndarray , np .ndarray | None ]:
145+ """Pad Qwen3-Omni video pixels to configured static grid limits when enabled.
146+
147+ Args:
148+ video_values: Video pixels of shape (batch, channels, T*tps, H*patch, W*patch).
149+ video_grid_thw: Actual video grid with shape (1, 3), in Qwen grid units.
150+ config: Config carrying video_max_grid_t/h/w and ViT patch sizes.
151+
152+ Returns:
153+ Tuple of:
154+ - padded video pixels, or the input when no max grid is configured
155+ - input grid_thw
156+ - pixel-level mask of shape (batch, 1, max_T*tps, max_H*patch, max_W*patch), or None
157+ """
158+ max_grid = (
159+ getattr (config , "video_max_grid_t" , None ),
160+ getattr (config , "video_max_grid_h" , None ),
161+ getattr (config , "video_max_grid_w" , None ),
162+ )
163+ if all (dim is None for dim in max_grid ):
164+ return video_values , video_grid_thw , None
165+ if any (dim is None for dim in max_grid ):
166+ raise ValueError ("video_max_grid_t, video_max_grid_h, and video_max_grid_w must be set together." )
167+ if video_values .ndim != 5 :
168+ raise ValueError (f"video_values must have shape (batch, channels, time, height, width), got { video_values .shape } ." )
169+
170+ max_t , max_h , max_w = (int (dim ) for dim in max_grid )
171+ actual_t , actual_h , actual_w = (int (dim ) for dim in video_grid_thw [0 ])
172+ if actual_t > max_t or actual_h > max_h or actual_w > max_w :
173+ raise ValueError (
174+ f"video grid { video_grid_thw [0 ].tolist ()} exceeds max grid { (max_t , max_h , max_w )} . "
175+ "Scale or resize the video before padding."
176+ )
177+
178+ temporal_patch_size = config .temporal_patch_size_for_vit
179+ patch_size = config .patch_size_for_vit
180+ valid_t_px = actual_t * temporal_patch_size
181+ valid_h_px = actual_h * patch_size
182+ valid_w_px = actual_w * patch_size
183+ max_t_px = max_t * temporal_patch_size
184+ max_h_px = max_h * patch_size
185+ max_w_px = max_w * patch_size
186+
187+ padded_video_values = np .zeros (
188+ (video_values .shape [0 ], video_values .shape [1 ], max_t_px , max_h_px , max_w_px ),
189+ dtype = video_values .dtype ,
190+ )
191+ padded_video_values [:, :, :valid_t_px , :valid_h_px , :valid_w_px ] = video_values [
192+ :, :, :valid_t_px , :valid_h_px , :valid_w_px
193+ ]
194+
195+ video_mask = np .zeros ((video_values .shape [0 ], 1 , max_t_px , max_h_px , max_w_px ), dtype = np .int32 )
196+ video_mask [:, :, :valid_t_px , :valid_h_px , :valid_w_px ] = 1
197+
198+ return padded_video_values , video_grid_thw , video_mask
199+
200+
139201def smart_resize (
140202 height : int , width : int , factor : int = 28 , min_pixels : int = 56 * 56 , max_pixels : int = 14 * 14 * 4 * 1280
141203):
@@ -589,8 +651,10 @@ def preprocess_mm_data_qwen3_omni(config):
589651 config .patch_size_for_vit * video_grid_thw [0 , 2 ],
590652 ),
591653 )
654+ video_values , video_grid_thw , video_mask = maybe_pad_video_values_to_max_grid (video_values , video_grid_thw , config )
592655 processor_outputs .video_values = video_values
593656 processor_outputs .video_grid_thw = video_grid_thw
657+ processor_outputs .video_mask = video_mask
594658 processor_outputs .video_second_per_grid = np .asarray ([config .temporal_patch_size_for_vit ], dtype = np .float32 )
595659 processor_outputs .num_videos = 1 # Only one video for now.
596660
0 commit comments