passionMan commited on
Commit
30b022c
·
verified ·
1 Parent(s): e73698b

Add image_processing_brain_ocr.py

Browse files
Files changed (1) hide show
  1. image_processing_brain_ocr.py +348 -0
image_processing_brain_ocr.py ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
+ # Modified from HunyuanVL image processor for BrainOCR.
4
+ """Image processor class for BrainOCR."""
5
+
6
+ # isort: skip_file
7
+ import math
8
+
9
+ import numpy as np
10
+ import torchvision.transforms as transforms
11
+ from transformers import AutoImageProcessor
12
+ from transformers.image_processing_utils import BaseImageProcessor, BatchFeature
13
+ from transformers.image_transforms import (
14
+ convert_to_rgb,
15
+ )
16
+ from transformers.image_utils import (
17
+ OPENAI_CLIP_MEAN,
18
+ OPENAI_CLIP_STD,
19
+ ChannelDimension,
20
+ ImageInput,
21
+ PILImageResampling,
22
+ make_flat_list_of_images,
23
+ make_list_of_images,
24
+ valid_images,
25
+ validate_preprocess_arguments,
26
+ )
27
+ from transformers.utils import TensorType, logging
28
+ from transformers.video_utils import VideoInput, make_batched_videos
29
+
30
+ logger = logging.get_logger(__name__)
31
+
32
+
33
+ def smart_resize(
34
+ height: int,
35
+ width: int,
36
+ factor: int = 16,
37
+ min_pixels: int = 512 * 512,
38
+ max_pixels: int = 2048 * 2048,
39
+ ):
40
+ """Rescales the image so that the following conditions are met:
41
+
42
+ 1. Both dimensions (height and width) are divisible by 'factor'.
43
+
44
+ 2. The total number of pixels is within the range ['min_pixels', 'max_pixels'].
45
+
46
+ 3. The aspect ratio of the image is maintained as closely as possible.
47
+
48
+ """
49
+ if max(height, width) / min(height, width) > 200:
50
+ raise ValueError(
51
+ "absolute aspect ratio must be smaller than 200, got "
52
+ f"{max(height, width) / min(height, width)}"
53
+ )
54
+ h_bar = round(height / factor) * factor
55
+ w_bar = round(width / factor) * factor
56
+ if h_bar * w_bar > max_pixels:
57
+ beta = math.sqrt((height * width) / max_pixels)
58
+ h_bar = max(factor, math.floor(height / beta / factor) * factor)
59
+ w_bar = max(factor, math.floor(width / beta / factor) * factor)
60
+ elif h_bar * w_bar < min_pixels:
61
+ beta = math.sqrt(min_pixels / (height * width))
62
+ h_bar = math.ceil(height * beta / factor) * factor
63
+ w_bar = math.ceil(width * beta / factor) * factor
64
+ return h_bar, w_bar
65
+
66
+
67
+ class BrainOCRImageProcessor(BaseImageProcessor):
68
+ model_input_names = [
69
+ "pixel_values",
70
+ "image_grid_thw",
71
+ "pixel_values_videos",
72
+ "video_grid_thw",
73
+ ]
74
+
75
+ def __init__(
76
+ self,
77
+ do_resize: bool = True,
78
+ size: dict[str, int] | None = None,
79
+ resample: PILImageResampling = PILImageResampling.BICUBIC,
80
+ do_rescale: bool = True,
81
+ rescale_factor: int | float = 1 / 255,
82
+ do_normalize: bool = True,
83
+ image_mean: float | list[float] | None = None,
84
+ image_std: float | list[float] | None = None,
85
+ do_convert_rgb: bool = True,
86
+ min_pixels: int | None = None,
87
+ max_pixels: int | None = None,
88
+ patch_size: int = 16,
89
+ temporal_patch_size: int = 2,
90
+ merge_size: int = 2,
91
+ **kwargs,
92
+ ) -> None:
93
+ super().__init__(**kwargs)
94
+ if size is not None and (
95
+ "shortest_edge" not in size or "longest_edge" not in size
96
+ ):
97
+ raise ValueError(
98
+ "size must contain 'shortest_edge' and 'longest_edge' keys."
99
+ )
100
+ else:
101
+ size = {"shortest_edge": 512 * 512, "longest_edge": 2048 * 2048}
102
+ if min_pixels is not None:
103
+ size["shortest_edge"] = min_pixels
104
+ if max_pixels is not None:
105
+ size["longest_edge"] = max_pixels
106
+ self.min_pixels = size["shortest_edge"]
107
+ self.max_pixels = size["longest_edge"]
108
+ self.size = size
109
+
110
+ self.do_resize = do_resize
111
+ self.resample = resample
112
+ self.do_rescale = do_rescale
113
+ self.rescale_factor = rescale_factor
114
+ self.do_normalize = do_normalize
115
+ self.image_mean = image_mean if image_mean is not None else OPENAI_CLIP_MEAN
116
+ self.image_std = image_std if image_std is not None else OPENAI_CLIP_STD
117
+
118
+ self.patch_size = patch_size
119
+ self.temporal_patch_size = temporal_patch_size
120
+ self.merge_size = merge_size
121
+ self.do_convert_rgb = do_convert_rgb
122
+
123
+ def _preprocess(
124
+ self,
125
+ images: ImageInput | VideoInput,
126
+ do_resize: bool | None = None,
127
+ size: dict[str, int] | None = None,
128
+ resample: PILImageResampling = None,
129
+ do_rescale: bool | None = None,
130
+ rescale_factor: float | None = None,
131
+ do_normalize: bool | None = None,
132
+ image_mean: float | list[float] | None = None,
133
+ image_std: float | list[float] | None = None,
134
+ patch_size: int = 16,
135
+ temporal_patch_size: int = 2,
136
+ merge_size: int = 2,
137
+ do_convert_rgb: bool | None = None,
138
+ data_format: ChannelDimension | None = ChannelDimension.FIRST,
139
+ input_data_format: str | ChannelDimension | None = None,
140
+ ):
141
+ images = make_list_of_images(images)
142
+
143
+ if do_convert_rgb:
144
+ images = [convert_to_rgb(image) for image in images]
145
+
146
+ width, height = images[0].width, images[0].height
147
+ resized_width, resized_height = width, height
148
+ processed_images = []
149
+ for image in images:
150
+ if do_resize:
151
+ resized_height, resized_width = smart_resize(
152
+ height=height,
153
+ width=width,
154
+ factor=patch_size * merge_size,
155
+ min_pixels=self.min_pixels,
156
+ max_pixels=self.max_pixels,
157
+ )
158
+ image = image.resize((resized_width, resized_height))
159
+
160
+ if do_normalize:
161
+ image = transforms.Compose(
162
+ [
163
+ transforms.ToTensor(),
164
+ transforms.Normalize(self.image_mean, self.image_std),
165
+ ]
166
+ )(image)
167
+ processed_images.append(image)
168
+
169
+ patches = np.array(processed_images)
170
+ channel = patches.shape[1]
171
+ grid_t = patches.shape[0] // temporal_patch_size
172
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
173
+ patches = patches.reshape(
174
+ 1,
175
+ channel,
176
+ grid_h // merge_size,
177
+ merge_size,
178
+ patch_size,
179
+ grid_w // merge_size,
180
+ merge_size,
181
+ patch_size,
182
+ )
183
+ patches = patches.transpose(0, 2, 3, 5, 6, 1, 4, 7)
184
+ flatten_patches = patches.reshape(
185
+ 1 * grid_h * grid_w, channel * patch_size * patch_size
186
+ )
187
+
188
+ return flatten_patches, (grid_t, grid_h, grid_w)
189
+
190
+ def preprocess(
191
+ self,
192
+ images: ImageInput,
193
+ videos: VideoInput = None,
194
+ do_resize: bool | None = None,
195
+ size: dict[str, int] | None = None,
196
+ min_pixels: int | None = None,
197
+ max_pixels: int | None = None,
198
+ resample: PILImageResampling = None,
199
+ do_rescale: bool | None = None,
200
+ rescale_factor: float | None = None,
201
+ do_normalize: bool | None = None,
202
+ image_mean: float | list[float] | None = None,
203
+ image_std: float | list[float] | None = None,
204
+ patch_size: int | None = None,
205
+ temporal_patch_size: int | None = None,
206
+ merge_size: int | None = None,
207
+ do_convert_rgb: bool | None = None,
208
+ return_tensors: str | TensorType | None = None,
209
+ data_format: ChannelDimension | None = ChannelDimension.FIRST,
210
+ input_data_format: str | ChannelDimension | None = None,
211
+ ):
212
+ min_pixels = min_pixels if min_pixels is not None else self.min_pixels
213
+ max_pixels = max_pixels if max_pixels is not None else self.max_pixels
214
+
215
+ if size is not None:
216
+ if "shortest_edge" not in size or "longest_edge" not in size:
217
+ raise ValueError(
218
+ "size must contain 'shortest_edge' and 'longest_edge' keys."
219
+ )
220
+ min_pixels = size["shortest_edge"]
221
+ elif min_pixels is not None and max_pixels is not None:
222
+ size = {"shortest_edge": min_pixels, "longest_edge": max_pixels}
223
+ else:
224
+ size = {**self.size}
225
+
226
+ do_resize = do_resize if do_resize is not None else self.do_resize
227
+ resample = resample if resample is not None else self.resample
228
+ do_rescale = do_rescale if do_rescale is not None else self.do_rescale
229
+ rescale_factor = (
230
+ rescale_factor if rescale_factor is not None else self.rescale_factor
231
+ )
232
+ do_normalize = do_normalize if do_normalize is not None else self.do_normalize
233
+ image_mean = image_mean if image_mean is not None else self.image_mean
234
+ image_std = image_std if image_std is not None else self.image_std
235
+ patch_size = patch_size if patch_size is not None else self.patch_size
236
+ temporal_patch_size = (
237
+ temporal_patch_size
238
+ if temporal_patch_size is not None
239
+ else self.temporal_patch_size
240
+ )
241
+ merge_size = merge_size if merge_size is not None else self.merge_size
242
+ do_convert_rgb = (
243
+ do_convert_rgb if do_convert_rgb is not None else self.do_convert_rgb
244
+ )
245
+
246
+ if images is not None:
247
+ images = make_flat_list_of_images(images)
248
+
249
+ if images is not None and not valid_images(images):
250
+ raise ValueError(
251
+ "Invalid image type. Must be of type PIL.Image.Image, numpy.ndarray, "
252
+ "torch.Tensor, tf.Tensor or jax.ndarray."
253
+ )
254
+
255
+ validate_preprocess_arguments(
256
+ rescale_factor=rescale_factor,
257
+ do_normalize=do_normalize,
258
+ image_mean=image_mean,
259
+ image_std=image_std,
260
+ do_resize=do_resize,
261
+ size=size,
262
+ resample=resample,
263
+ )
264
+
265
+ data = {}
266
+ if images is not None:
267
+ pixel_values, vision_grid_thws = [], []
268
+ for image in images:
269
+ patches, image_grid_thw = self._preprocess(
270
+ image,
271
+ do_resize=do_resize,
272
+ size=size,
273
+ resample=resample,
274
+ do_rescale=do_rescale,
275
+ rescale_factor=rescale_factor,
276
+ do_normalize=do_normalize,
277
+ image_mean=image_mean,
278
+ image_std=image_std,
279
+ patch_size=patch_size,
280
+ temporal_patch_size=temporal_patch_size,
281
+ merge_size=merge_size,
282
+ data_format=data_format,
283
+ do_convert_rgb=do_convert_rgb,
284
+ input_data_format=input_data_format,
285
+ )
286
+ pixel_values.extend(patches)
287
+ vision_grid_thws.append(image_grid_thw)
288
+ pixel_values = np.array(pixel_values)
289
+ vision_grid_thws = np.array(vision_grid_thws)
290
+ data.update(
291
+ {"pixel_values": pixel_values, "image_grid_thw": vision_grid_thws}
292
+ )
293
+
294
+ if videos is not None:
295
+ videos = make_batched_videos(videos)
296
+ pixel_values_videos, vision_grid_thws_videos = [], []
297
+ for images in videos:
298
+ patches, video_grid_thw = self._preprocess(
299
+ images,
300
+ do_resize=do_resize,
301
+ size=size,
302
+ resample=resample,
303
+ do_rescale=do_rescale,
304
+ rescale_factor=rescale_factor,
305
+ do_normalize=do_normalize,
306
+ image_mean=image_mean,
307
+ image_std=image_std,
308
+ patch_size=patch_size,
309
+ temporal_patch_size=temporal_patch_size,
310
+ merge_size=merge_size,
311
+ data_format=data_format,
312
+ do_convert_rgb=do_convert_rgb,
313
+ input_data_format=input_data_format,
314
+ )
315
+ pixel_values_videos.extend(patches)
316
+ vision_grid_thws_videos.append(video_grid_thw)
317
+ data.update(
318
+ {
319
+ "pixel_values_videos": np.array(pixel_values_videos),
320
+ "video_grid_thw": np.array(vision_grid_thws_videos),
321
+ }
322
+ )
323
+
324
+ return BatchFeature(data=data, tensor_type=return_tensors)
325
+
326
+ def get_number_of_image_patches(self, height: int, width: int, images_kwargs=None):
327
+ min_pixels = (
328
+ images_kwargs["min_pixels"]
329
+ if "min_pixels" in images_kwargs
330
+ else self.size["shortest_edge"]
331
+ )
332
+ max_pixels = (
333
+ images_kwargs["max_pixels"]
334
+ if "max_pixels" in images_kwargs
335
+ else self.size["longest_edge"]
336
+ )
337
+ patch_size = images_kwargs.get("patch_size", self.patch_size)
338
+ merge_size = images_kwargs.get("merge_size", self.merge_size)
339
+
340
+ factor = patch_size * merge_size
341
+ resized_height, resized_width = smart_resize(
342
+ height, width, factor, min_pixels=min_pixels, max_pixels=max_pixels
343
+ )
344
+ grid_h, grid_w = resized_height // patch_size, resized_width // patch_size
345
+ return grid_h * (grid_w + 1) + 2
346
+
347
+
348
+ AutoImageProcessor.register("BrainOCRImageProcessor", BrainOCRImageProcessor)