YummyYum commited on
Commit
56e877f
·
verified ·
1 Parent(s): 381356b

Upload video_processor.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. video_processor.py +208 -0
video_processor.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2023-2024 SGLang Team
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ """
4
+ MiniMax VL family HuggingFace-compatible VideoProcessor.
5
+ """
6
+
7
+ import math
8
+ from typing import List, Optional, Tuple, Union
9
+
10
+ import torch
11
+ import torchvision
12
+ from torchvision.transforms import InterpolationMode
13
+ from transformers import BatchFeature
14
+ from transformers.image_utils import PILImageResampling, SizeDict
15
+ from transformers.processing_utils import (
16
+ Unpack,
17
+ VideosKwargs,
18
+ )
19
+ from transformers.utils import TensorType
20
+ from transformers.video_processing_utils import BaseVideoProcessor
21
+ from transformers.video_utils import group_videos_by_shape, reorder_videos
22
+
23
+ MAX_RATIO = 200
24
+
25
+
26
+ def round_by_factor(number: int, factor: int) -> int:
27
+ return round(number / factor) * factor
28
+
29
+
30
+ def ceil_by_factor(number: int, factor: int) -> int:
31
+ return math.ceil(number / factor) * factor
32
+
33
+
34
+ def floor_by_factor(number: int, factor: int) -> int:
35
+ return math.floor(number / factor) * factor
36
+
37
+
38
+ def smart_resize(
39
+ height: int,
40
+ width: int,
41
+ factor: int = 28,
42
+ min_pixels: int = 4 * 28 * 28,
43
+ max_pixels: int = 451584,
44
+ ) -> tuple[int, int]:
45
+ if max(height, width) / min(height, width) > MAX_RATIO:
46
+ raise ValueError(
47
+ f"absolute aspect ratio must be smaller than {MAX_RATIO}, "
48
+ f"got {max(height, width) / min(height, width)}"
49
+ )
50
+ h_bar = max(factor, round_by_factor(height, factor))
51
+ w_bar = max(factor, round_by_factor(width, factor))
52
+ if h_bar * w_bar > max_pixels:
53
+ beta = math.sqrt((height * width) / max_pixels)
54
+ h_bar = floor_by_factor(height / beta, factor)
55
+ w_bar = floor_by_factor(width / beta, factor)
56
+ elif h_bar * w_bar < min_pixels:
57
+ beta = math.sqrt(min_pixels / (height * width))
58
+ h_bar = ceil_by_factor(height * beta, factor)
59
+ w_bar = ceil_by_factor(width * beta, factor)
60
+ return h_bar, w_bar
61
+
62
+
63
+ class MiniMaxM3VLVideoProcessorKwargs(VideosKwargs, total=False):
64
+ patch_size: int
65
+ temporal_patch_size: int
66
+ merge_size: int
67
+ min_pixels: int
68
+ max_pixels: int
69
+ total_pixels: int
70
+ min_frames: int
71
+ max_frames: int
72
+ fps: float | int
73
+
74
+
75
+ class MiniMaxM3VLVideoProcessor(BaseVideoProcessor):
76
+ do_resize = True
77
+ resample = PILImageResampling.BICUBIC
78
+ size = {"height": 672, "width": 672}
79
+ default_to_square = False
80
+ do_rescale = True
81
+ rescale_factor = 1 / 255
82
+ do_normalize = True
83
+ image_mean = [0.48145466, 0.4578275, 0.40821073]
84
+ image_std = [0.26862954, 0.26130258, 0.27577711]
85
+ do_convert_rgb = True
86
+ do_sample_frames = False
87
+ patch_size = 14
88
+ temporal_patch_size = 2
89
+ merge_size = 2
90
+ min_pixels = 4 * 28 * 28
91
+ max_pixels = 768 * 28 * 28 # 602,112
92
+ total_pixels = int(64000 * 28 * 28 * 0.9) # ~45M, ~64k tokens budget
93
+ fps = 1.0
94
+ min_frames = 4
95
+ max_frames = 768
96
+ valid_kwargs = MiniMaxM3VLVideoProcessorKwargs
97
+ model_input_names = ["pixel_values_videos", "video_grid_thw"]
98
+
99
+ def __init__(self, **kwargs: Unpack[MiniMaxM3VLVideoProcessorKwargs]):
100
+ super().__init__(**kwargs)
101
+
102
+ def _preprocess(
103
+ self,
104
+ videos: List[torch.Tensor],
105
+ do_convert_rgb: bool,
106
+ do_resize: bool,
107
+ size: SizeDict,
108
+ resample: PILImageResampling | InterpolationMode | int | None,
109
+ do_rescale: bool,
110
+ rescale_factor: float,
111
+ do_normalize: bool,
112
+ image_mean: float | List[float] | None,
113
+ image_std: float | List[float] | None,
114
+ patch_size: int,
115
+ temporal_patch_size: int,
116
+ merge_size: int,
117
+ min_pixels: int,
118
+ max_pixels: int,
119
+ return_tensors: str | TensorType | None = None,
120
+ **kwargs,
121
+ ) -> BatchFeature:
122
+ grouped_videos, grouped_videos_index = group_videos_by_shape(videos)
123
+ resized_videos_grouped = {}
124
+ factor = patch_size * merge_size
125
+ for shape, stacked_videos in grouped_videos.items():
126
+ batch_size, num_frames, channels, height, width = stacked_videos.shape
127
+ resized_height, resized_width = height, width
128
+ if do_resize:
129
+ resized_height, resized_width = smart_resize(
130
+ height, width, factor=factor,
131
+ min_pixels=min_pixels, max_pixels=max_pixels,
132
+ )
133
+ stacked_videos = stacked_videos.view(
134
+ batch_size * num_frames, channels, height, width
135
+ )
136
+ stacked_videos = self.resize(
137
+ stacked_videos,
138
+ size=SizeDict(height=resized_height, width=resized_width),
139
+ resample=resample,
140
+ )
141
+ stacked_videos = stacked_videos.view(
142
+ batch_size,
143
+ num_frames,
144
+ channels,
145
+ resized_height,
146
+ resized_width,
147
+ )
148
+ resized_videos_grouped[shape] = stacked_videos
149
+ resized_videos = reorder_videos(resized_videos_grouped, grouped_videos_index)
150
+
151
+ grouped_videos, grouped_videos_index = group_videos_by_shape(resized_videos)
152
+ processed_videos_grouped = {}
153
+ processed_grids = {}
154
+ for shape, stacked_videos in grouped_videos.items():
155
+ resized_height, resized_width = stacked_videos.shape[-2:]
156
+ patches = self.rescale_and_normalize(
157
+ stacked_videos,
158
+ do_rescale,
159
+ rescale_factor,
160
+ do_normalize,
161
+ image_mean,
162
+ image_std,
163
+ )
164
+
165
+ if pad := -patches.shape[1] % temporal_patch_size:
166
+ repeats = patches[:, -1:].expand(-1, pad, -1, -1, -1)
167
+ patches = torch.cat([patches, repeats], dim=1)
168
+
169
+ batch_size, grid_t, channels = patches.shape[:3]
170
+ grid_t = grid_t // temporal_patch_size
171
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
172
+
173
+ patches = patches.view(
174
+ batch_size,
175
+ grid_t,
176
+ temporal_patch_size,
177
+ channels,
178
+ grid_h // merge_size,
179
+ merge_size,
180
+ patch_size,
181
+ grid_w // merge_size,
182
+ merge_size,
183
+ patch_size,
184
+ )
185
+ patches = patches.permute(0, 1, 4, 7, 5, 8, 3, 2, 6, 9)
186
+ flatten_patches = patches.reshape(
187
+ batch_size,
188
+ grid_t * grid_h * grid_w,
189
+ channels * temporal_patch_size * patch_size * patch_size,
190
+ )
191
+
192
+ processed_videos_grouped[shape] = flatten_patches
193
+ processed_grids[shape] = [[grid_t, grid_h, grid_w]] * batch_size
194
+
195
+ processed_videos = reorder_videos(
196
+ processed_videos_grouped, grouped_videos_index
197
+ )
198
+ processed_grids = reorder_videos(processed_grids, grouped_videos_index)
199
+ pixel_values_videos = torch.cat(processed_videos, dim=0)
200
+ video_grid_thw = torch.tensor(processed_grids, dtype=torch.long)
201
+
202
+ return BatchFeature(
203
+ data={
204
+ "pixel_values_videos": pixel_values_videos,
205
+ "video_grid_thw": video_grid_thw,
206
+ },
207
+ tensor_type=return_tensors,
208
+ )