zhiyucheng commited on
Commit
7fdd3ba
·
verified ·
1 Parent(s): 215db0b

Copy image_processor.py from nvidia/MiniMax-M3-NVFP4-Preview0615

Browse files
Files changed (1) hide show
  1. image_processor.py +223 -0
image_processor.py ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2023-2024 SGLang Team
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ """
4
+ MiniMax VL family HuggingFace-compatible Processor, ImageProcessor, VideoProcessor.
5
+ """
6
+ import math
7
+ from typing import List, Tuple
8
+
9
+ import torch
10
+ from torchvision.transforms import InterpolationMode
11
+ from transformers import BatchFeature
12
+ from transformers.image_processing_utils_fast import (
13
+ BaseImageProcessorFast,
14
+ group_images_by_shape,
15
+ reorder_images,
16
+ )
17
+ from transformers.image_utils import PILImageResampling, SizeDict
18
+ from transformers.processing_utils import (
19
+ ImagesKwargs,
20
+ Unpack,
21
+ )
22
+ from transformers.utils import TensorType
23
+
24
+ MAX_RATIO = 200
25
+
26
+
27
+ def round_by_factor(number: int, factor: int) -> int:
28
+ return round(number / factor) * factor
29
+
30
+
31
+ def ceil_by_factor(number: int, factor: int) -> int:
32
+ return math.ceil(number / factor) * factor
33
+
34
+
35
+ def floor_by_factor(number: int, factor: int) -> int:
36
+ return math.floor(number / factor) * factor
37
+
38
+
39
+ def smart_resize(
40
+ height: int,
41
+ width: int,
42
+ factor: int = 28,
43
+ min_pixels: int = 4 * 28 * 28,
44
+ max_pixels: int = 451584,
45
+ ) -> tuple[int, int]:
46
+ if max(height, width) / min(height, width) > MAX_RATIO:
47
+ raise ValueError(
48
+ f"absolute aspect ratio must be smaller than {MAX_RATIO}, "
49
+ f"got {max(height, width) / min(height, width)}"
50
+ )
51
+ h_bar = max(factor, round_by_factor(height, factor))
52
+ w_bar = max(factor, round_by_factor(width, factor))
53
+ if h_bar * w_bar > max_pixels:
54
+ beta = math.sqrt((height * width) / max_pixels)
55
+ h_bar = floor_by_factor(height / beta, factor)
56
+ w_bar = floor_by_factor(width / beta, factor)
57
+ elif h_bar * w_bar < min_pixels:
58
+ beta = math.sqrt(min_pixels / (height * width))
59
+ h_bar = ceil_by_factor(height * beta, factor)
60
+ w_bar = ceil_by_factor(width * beta, factor)
61
+ return h_bar, w_bar
62
+
63
+
64
+ # ==============================================================================
65
+ # MiniMax M3 VL Image Processor Fast (Fast Mode - Torch based)
66
+ # ==============================================================================
67
+
68
+
69
+ class MiniMaxM3VLImageProcessorKwargs(ImagesKwargs, total=False):
70
+ patch_size: int
71
+ temporal_patch_size: int
72
+ merge_size: int
73
+ max_pixels: int
74
+
75
+
76
+ class MiniMaxM3VLImageProcessor(BaseImageProcessorFast):
77
+ do_resize = True
78
+ resample = PILImageResampling.BICUBIC
79
+ size = {"height": 672, "width": 672} # required by base class validation, not used as resize bound
80
+ default_to_square = False
81
+ do_rescale = True
82
+ rescale_factor = 1 / 255
83
+ do_normalize = True
84
+ image_mean = [0.48145466, 0.4578275, 0.40821073]
85
+ image_std = [0.26862954, 0.26130258, 0.27577711]
86
+ do_convert_rgb = True
87
+ patch_size = 14
88
+ temporal_patch_size = 2
89
+ merge_size = 2
90
+ max_pixels = 451584 # 672*672
91
+ valid_kwargs = MiniMaxM3VLImageProcessorKwargs
92
+ model_input_names = ["pixel_values", "image_grid_thw"]
93
+
94
+ def __init__(self, **kwargs: Unpack[MiniMaxM3VLImageProcessorKwargs]):
95
+ super().__init__(**kwargs)
96
+
97
+ def preprocess(
98
+ self, images, **kwargs: Unpack[MiniMaxM3VLImageProcessorKwargs]
99
+ ) -> BatchFeature:
100
+ return super().preprocess(images, **kwargs)
101
+
102
+ def _preprocess(
103
+ self,
104
+ images: List[torch.Tensor],
105
+ do_resize: bool,
106
+ size: SizeDict,
107
+ resample: PILImageResampling | InterpolationMode | int | None,
108
+ do_rescale: bool,
109
+ rescale_factor: float,
110
+ do_normalize: bool,
111
+ image_mean: float | List[float] | None,
112
+ image_std: float | List[float] | None,
113
+ patch_size: int,
114
+ temporal_patch_size: int,
115
+ merge_size: int,
116
+ max_pixels: int,
117
+ disable_grouping: bool | None,
118
+ return_tensors: str | TensorType | None,
119
+ **kwargs,
120
+ ) -> BatchFeature:
121
+ grouped_images, grouped_images_index = group_images_by_shape(
122
+ images, disable_grouping=disable_grouping
123
+ )
124
+ resized_images_grouped = {}
125
+ factor = patch_size * merge_size
126
+ for shape, stacked_images in grouped_images.items():
127
+ height, width = stacked_images.shape[-2:]
128
+ if do_resize:
129
+ resized_height, resized_width = smart_resize(
130
+ height, width, factor=factor,
131
+ max_pixels=max_pixels,
132
+ )
133
+ stacked_images = self.resize(
134
+ stacked_images,
135
+ size=SizeDict(height=resized_height, width=resized_width),
136
+ resample=resample,
137
+ )
138
+ resized_images_grouped[shape] = stacked_images
139
+
140
+ resized_images = reorder_images(resized_images_grouped, grouped_images_index)
141
+
142
+ grouped_images, grouped_images_index = group_images_by_shape(
143
+ resized_images, disable_grouping=disable_grouping
144
+ )
145
+ processed_images_grouped = {}
146
+ processed_grids = {}
147
+
148
+ for shape, stacked_images in grouped_images.items():
149
+ resized_height, resized_width = stacked_images.shape[-2:]
150
+
151
+ patches = self.rescale_and_normalize(
152
+ stacked_images,
153
+ do_rescale,
154
+ rescale_factor,
155
+ do_normalize,
156
+ image_mean,
157
+ image_std,
158
+ )
159
+ if patches.ndim == 4:
160
+ patches = patches.unsqueeze(1)
161
+
162
+ if patches.shape[1] % temporal_patch_size != 0:
163
+ repeats = patches[:, -1:].repeat(
164
+ 1,
165
+ temporal_patch_size - (patches.shape[1] % temporal_patch_size),
166
+ 1,
167
+ 1,
168
+ 1,
169
+ )
170
+ patches = torch.cat([patches, repeats], dim=1)
171
+
172
+ batch_size, grid_t, channel = patches.shape[:3]
173
+ grid_t = grid_t // temporal_patch_size
174
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
175
+
176
+ patches = patches.view(
177
+ batch_size,
178
+ grid_t,
179
+ temporal_patch_size,
180
+ channel,
181
+ grid_h // merge_size,
182
+ merge_size,
183
+ patch_size,
184
+ grid_w // merge_size,
185
+ merge_size,
186
+ patch_size,
187
+ )
188
+ patches = patches.permute(0, 1, 4, 7, 5, 8, 3, 2, 6, 9)
189
+
190
+ flatten_patches = patches.reshape(
191
+ batch_size,
192
+ grid_t * grid_h * grid_w,
193
+ channel * temporal_patch_size * patch_size * patch_size,
194
+ )
195
+
196
+ processed_images_grouped[shape] = flatten_patches
197
+ processed_grids[shape] = [[grid_t, grid_h, grid_w]] * batch_size
198
+
199
+ processed_images = reorder_images(
200
+ processed_images_grouped, grouped_images_index
201
+ )
202
+ processed_grids = reorder_images(processed_grids, grouped_images_index)
203
+
204
+ pixel_values = torch.cat(processed_images, dim=0)
205
+ image_grid_thw = torch.tensor(processed_grids, dtype=torch.long)
206
+
207
+ return BatchFeature(
208
+ data={"pixel_values": pixel_values, "image_grid_thw": image_grid_thw},
209
+ tensor_type=return_tensors,
210
+ )
211
+
212
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs=None):
213
+ images_kwargs = images_kwargs or {}
214
+ patch_size = images_kwargs.get("patch_size", self.patch_size)
215
+ merge_size = images_kwargs.get("merge_size", self.merge_size)
216
+ max_pixels = images_kwargs.get("max_pixels", self.max_pixels)
217
+
218
+ resized_height, resized_width = smart_resize(
219
+ height, width, factor=patch_size * merge_size,
220
+ max_pixels=max_pixels,
221
+ )
222
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
223
+ return grid_h * grid_w