chugr 0.2.0.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. chugr/__init__.py +46 -0
  2. chugr/app/test.py +105 -0
  3. chugr/common/__init__.py +9 -0
  4. chugr/common/cli.py +234 -0
  5. chugr/common/collate.py +104 -0
  6. chugr/common/config.py +335 -0
  7. chugr/common/random.py +76 -0
  8. chugr/common/serialize.py +150 -0
  9. chugr/common/task_config.py +24 -0
  10. chugr/common/types.py +122 -0
  11. chugr/common/urls.py +88 -0
  12. chugr/doc/__init__.py +17 -0
  13. chugr/doc/constants.py +15 -0
  14. chugr/doc/doc_processor.py +248 -0
  15. chugr/doc/doc_read_processor.py +249 -0
  16. chugr/doc/doc_vqa_processor.py +113 -0
  17. chugr/doc/synth_text.py +212 -0
  18. chugr/hfds/__init__.py +2 -0
  19. chugr/hfds/collate.py +83 -0
  20. chugr/hfds/loader.py +385 -0
  21. chugr/hfds/wrappers.py +108 -0
  22. chugr/image/__init__.py +4 -0
  23. chugr/image/build_transforms_doc.py +427 -0
  24. chugr/image/build_transforms_image.py +203 -0
  25. chugr/image/transforms_alb.py +112 -0
  26. chugr/image/transforms_factory.py +58 -0
  27. chugr/image/transforms_torch.py +137 -0
  28. chugr/loader.py +249 -0
  29. chugr/naflex.py +357 -0
  30. chugr/task_pipeline/__init__.py +7 -0
  31. chugr/task_pipeline/pipeline_doc_read.py +66 -0
  32. chugr/task_pipeline/pipeline_doc_vqa.py +124 -0
  33. chugr/task_pipeline/pipeline_factory.py +32 -0
  34. chugr/task_pipeline/pipeline_gtparse.py +93 -0
  35. chugr/task_pipeline/pipeline_image_text.py +90 -0
  36. chugr/task_pipeline/pipeline_manual.py +27 -0
  37. chugr/text/__init__.py +1 -0
  38. chugr/text/tokenization.py +222 -0
  39. chugr/version.py +1 -0
  40. chugr/wds/__init__.py +7 -0
  41. chugr/wds/dataset_info.py +65 -0
  42. chugr/wds/decode.py +319 -0
  43. chugr/wds/filters.py +142 -0
  44. chugr/wds/helpers.py +68 -0
  45. chugr/wds/loader.py +187 -0
  46. chugr/wds/pipeline.py +182 -0
  47. chugr/wds/shardlists.py +134 -0
  48. chugr/wds/tariterators.py +70 -0
  49. chugr-0.2.0.dev0.dist-info/METADATA +436 -0
  50. chugr-0.2.0.dev0.dist-info/RECORD +53 -0
  51. chugr-0.2.0.dev0.dist-info/WHEEL +4 -0
  52. chugr-0.2.0.dev0.dist-info/entry_points.txt +4 -0
  53. chugr-0.2.0.dev0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,427 @@
1
+ import random
2
+ from typing import Optional
3
+
4
+ import torchvision.transforms.functional as F
5
+ from torchvision import transforms
6
+
7
+ from chugr.common import ImageInputCfg, ImageAugCfg
8
+
9
+ from .transforms_torch import AlignLongAxis, Bitmap, Erosion, Dilation, CropMargin
10
+
11
+ # NOTE, chugr currently depends on some time aug impl, this should be flipped if timm ends up
12
+ # leveraging chugr data pipelines.
13
+ from timm.data import str_to_interp_mode, ResizeKeepRatio, CenterCropOrPad, RandomCropOrPad
14
+ from timm.data.naflex_transforms import Patchify, get_image_size_for_seq
15
+
16
+
17
+ _DOC_AUG_DEFAULTS = dict(
18
+ scale_prob=0.05,
19
+ scale_range=(0.85, 1.04),
20
+ ratio_prob=0.05,
21
+ ratio_range=(.9, 1.11),
22
+ bitmap_prob=0.05,
23
+ erosion_dilation_prob=0.02,
24
+ shear_prob=0.05,
25
+ shear_range_x=(0, 3.),
26
+ shear_range_y=(-3, 0),
27
+ shift_scale_rotate_prob=0.03,
28
+ shift_range_x=0.04,
29
+ shift_range_y=0.03,
30
+ rotate_range=3,
31
+ elastic_prob=0.04,
32
+ elastic_alpha=50.,
33
+ elastic_sigma=12.,
34
+ brightness_contrast_prob=0.04,
35
+ brightness_range=0.1,
36
+ contrast_range=0.1,
37
+ gaussian_blur_prob=0.03,
38
+ gaussian_blur_kernel=3,
39
+ )
40
+
41
+
42
+ def _doc_aug_ops(params, interpolation_mode, fill):
43
+ """ Torchvision based document augmentations applied to an image already at (roughly) target size. """
44
+ return [
45
+ transforms.RandomApply([Bitmap()], p=params['bitmap_prob']),
46
+ transforms.RandomApply([
47
+ transforms.RandomChoice([
48
+ Erosion(3),
49
+ Dilation(3),
50
+ ])],
51
+ p=params['erosion_dilation_prob']
52
+ ),
53
+ transforms.RandomApply([
54
+ transforms.RandomAffine(
55
+ degrees=0,
56
+ shear=params['shear_range_x'] + params['shear_range_y'],
57
+ interpolation=interpolation_mode,
58
+ fill=fill,
59
+ )],
60
+ p=params['shear_prob'],
61
+ ),
62
+ transforms.RandomApply([
63
+ transforms.RandomAffine(
64
+ degrees=params['rotate_range'],
65
+ translate=(params['shift_range_x'], params['shift_range_y']),
66
+ interpolation=interpolation_mode,
67
+ fill=fill,
68
+ )],
69
+ p=params['shift_scale_rotate_prob'],
70
+ ),
71
+ transforms.RandomApply([
72
+ transforms.ElasticTransform(
73
+ alpha=params['elastic_alpha'],
74
+ sigma=params['elastic_sigma'],
75
+ interpolation=interpolation_mode,
76
+ fill=fill,
77
+ )],
78
+ p=params['elastic_prob'],
79
+ ),
80
+ transforms.RandomApply([
81
+ transforms.ColorJitter(
82
+ brightness=params['brightness_range'],
83
+ contrast=params['contrast_range'],
84
+ )],
85
+ p=params['brightness_contrast_prob'],
86
+ ),
87
+ transforms.RandomApply([
88
+ transforms.GaussianBlur(
89
+ params['gaussian_blur_kernel'],
90
+ sigma=(0.1, 0.8),
91
+ )],
92
+ p=params['gaussian_blur_prob'],
93
+ ),
94
+ ]
95
+
96
+
97
+ def build_transforms_doc_basic(
98
+ input_cfg: ImageInputCfg,
99
+ is_training: bool = False,
100
+ do_normalize: bool = True,
101
+ aug_cfg: Optional[ImageAugCfg] = None,
102
+ composed: bool = True,
103
+ ):
104
+ # minimal fixed size doc transforms, resize keeping aspect + pad (random pad position for train)
105
+ image_size = input_cfg.size
106
+ interpolation_mode = str_to_interp_mode(input_cfg.interpolation)
107
+
108
+ pp = []
109
+
110
+ if input_cfg.crop_margin:
111
+ pp += [CropMargin()]
112
+
113
+ if input_cfg.align_long_axis:
114
+ pp += [AlignLongAxis(image_size, interpolation=interpolation_mode)]
115
+
116
+ pp += [ResizeKeepRatio(image_size, longest=1, interpolation=input_cfg.interpolation)]
117
+ if is_training:
118
+ pp += [RandomCropOrPad(image_size, fill=input_cfg.fill_color)]
119
+ else:
120
+ pp += [CenterCropOrPad(image_size, fill=input_cfg.fill_color)]
121
+
122
+ pp += [transforms.ToTensor()]
123
+
124
+ if do_normalize:
125
+ pp += [transforms.Normalize(input_cfg.mean, input_cfg.std)]
126
+
127
+ return transforms.Compose(pp) if composed else pp
128
+
129
+
130
+ def build_transforms_doc_better(
131
+ input_cfg: ImageInputCfg,
132
+ is_training: bool = False,
133
+ do_normalize: bool = True,
134
+ aug_cfg: Optional[ImageAugCfg] = None,
135
+ composed: bool = True,
136
+ ):
137
+ # an improved torchvision + custom op transforms (no albumentations)
138
+ image_size = input_cfg.size
139
+ interpolation_mode = str_to_interp_mode(input_cfg.interpolation)
140
+ pp = []
141
+
142
+ if input_cfg.crop_margin:
143
+ pp += [CropMargin()]
144
+
145
+ if input_cfg.align_long_axis:
146
+ pp += [AlignLongAxis(image_size, interpolation=interpolation_mode)]
147
+
148
+ if is_training:
149
+ # FIXME merge defaults w/ aug_cfg
150
+ params = dict(_DOC_AUG_DEFAULTS)
151
+ pp += [
152
+ ResizeKeepRatio(
153
+ image_size,
154
+ longest=1,
155
+ interpolation=input_cfg.interpolation,
156
+ random_scale_prob=params['scale_prob'],
157
+ random_scale_range=params['scale_range'],
158
+ random_aspect_prob=params['ratio_prob'],
159
+ random_aspect_range=params['ratio_range'],
160
+ ),
161
+ ]
162
+ pp += _doc_aug_ops(params, interpolation_mode, input_cfg.fill_color)
163
+ pp += [
164
+ RandomCropOrPad(image_size, fill=input_cfg.fill_color),
165
+ transforms.CenterCrop(image_size),
166
+ ]
167
+ else:
168
+ pp += [
169
+ ResizeKeepRatio(image_size, longest=1, interpolation=input_cfg.interpolation),
170
+ CenterCropOrPad(image_size, fill=input_cfg.fill_color),
171
+ ]
172
+
173
+ pp += [transforms.ToTensor()]
174
+
175
+ if do_normalize:
176
+ pp += [transforms.Normalize(input_cfg.mean, input_cfg.std)]
177
+
178
+ return transforms.Compose(pp) if composed else pp
179
+
180
+
181
+ class ResizeToSeqLen:
182
+ """ Resize (preserving aspect) to the largest size whose patch count fits within a sequence length budget.
183
+
184
+ Optionally (train) sample a reduced budget to vary the resolution.
185
+ """
186
+
187
+ def __init__(
188
+ self,
189
+ patch_size: int,
190
+ max_seq_len: int,
191
+ interpolation: str = 'bicubic',
192
+ random_seq_len_prob: float = 0.,
193
+ random_seq_len_range=(0.5, 1.0),
194
+ max_ratio: Optional[float] = None,
195
+ ):
196
+ self.patch_size = patch_size
197
+ self.max_seq_len = max_seq_len
198
+ self.interpolation = str_to_interp_mode(interpolation)
199
+ self.random_seq_len_prob = random_seq_len_prob
200
+ self.random_seq_len_range = random_seq_len_range
201
+ self.max_ratio = max_ratio
202
+
203
+ def __call__(self, img):
204
+ w, h = F.get_image_size(img)
205
+ seq_len = self.max_seq_len
206
+ if self.random_seq_len_prob > 0 and random.random() < self.random_seq_len_prob:
207
+ seq_len = max(1, int(seq_len * random.uniform(*self.random_seq_len_range)))
208
+ _, (th, tw) = get_image_size_for_seq(
209
+ (h, w),
210
+ patch_size=self.patch_size,
211
+ max_seq_len=seq_len,
212
+ divisible_by_patch=True,
213
+ max_ratio=self.max_ratio,
214
+ )
215
+ return F.resize(img, [th, tw], interpolation=self.interpolation, antialias=True)
216
+
217
+ def __repr__(self):
218
+ return (f'{self.__class__.__name__}(patch_size={self.patch_size}, max_seq_len={self.max_seq_len}, '
219
+ f'random_seq_len_prob={self.random_seq_len_prob})')
220
+
221
+
222
+ def build_transforms_doc_naflex(
223
+ input_cfg: ImageInputCfg,
224
+ is_training: bool = False,
225
+ do_normalize: bool = True,
226
+ aug_cfg: Optional[ImageAugCfg] = None,
227
+ composed: bool = True,
228
+ ):
229
+ """ Native aspect, variable resolution (NaFlex) document transforms.
230
+
231
+ The page is resized (aspect preserved) to fit a patch budget (input_cfg.max_seq_len), augmented, then
232
+ split into a flat sequence of patches w/ (y, x) patch coordinates. Output is a dict that the chugr
233
+ collate fn pads to the longest sequence in the batch.
234
+ """
235
+ assert input_cfg.patch_size, 'patch_size must be set in ImageInputCfg for naflex transforms.'
236
+ assert input_cfg.max_seq_len, 'max_seq_len must be set in ImageInputCfg for naflex transforms.'
237
+ interpolation_mode = str_to_interp_mode(input_cfg.interpolation)
238
+ pp = []
239
+
240
+ if input_cfg.crop_margin:
241
+ pp += [CropMargin()]
242
+
243
+ if is_training:
244
+ params = dict(_DOC_AUG_DEFAULTS)
245
+ params.update(random_seq_len_prob=0.0, random_seq_len_range=(0.6, 1.0))
246
+ pp += [
247
+ ResizeToSeqLen(
248
+ input_cfg.patch_size,
249
+ input_cfg.max_seq_len,
250
+ interpolation=input_cfg.interpolation,
251
+ random_seq_len_prob=params['random_seq_len_prob'],
252
+ random_seq_len_range=params['random_seq_len_range'],
253
+ )
254
+ ]
255
+ pp += _doc_aug_ops(params, interpolation_mode, input_cfg.fill_color)
256
+ else:
257
+ pp += [
258
+ ResizeToSeqLen(
259
+ input_cfg.patch_size,
260
+ input_cfg.max_seq_len,
261
+ interpolation=input_cfg.interpolation,
262
+ )
263
+ ]
264
+
265
+ pp += [transforms.ToTensor()]
266
+
267
+ if do_normalize:
268
+ pp += [transforms.Normalize(input_cfg.mean, input_cfg.std)]
269
+
270
+ pp += [Patchify(input_cfg.patch_size)]
271
+
272
+ return transforms.Compose(pp) if composed else pp
273
+
274
+
275
+ def build_transforms_doc_nougat(
276
+ input_cfg: ImageInputCfg,
277
+ is_training: bool = False,
278
+ do_normalize: bool = True,
279
+ aug_cfg: Optional[ImageAugCfg] = None,
280
+ composed: bool = True,
281
+ ):
282
+ import albumentations as alb
283
+ from chugr.image.transforms_alb import BitmapAlb, ErosionAlb, DilationAlb, AlbWrapper, CropMarginCv2
284
+
285
+ # albumentations + custom opencv transforms from nougat
286
+ image_size = input_cfg.size
287
+ if input_cfg.interpolation == 'bilinear':
288
+ interpolation_mode = 1
289
+ else:
290
+ interpolation_mode = 2 # bicubic
291
+ border_mode = 0
292
+ fill = input_cfg.fill_color
293
+ if isinstance(fill, (list, tuple)) and len(fill) == 1:
294
+ fill = fill[0]
295
+
296
+ tv_pp = []
297
+ alb_pp = []
298
+
299
+ if input_cfg.crop_margin:
300
+ tv_pp += [CropMarginCv2()]
301
+
302
+ if input_cfg.align_long_axis:
303
+ tv_pp += [AlignLongAxis(image_size)]
304
+
305
+ if is_training:
306
+ # FIXME merge defaults w/ aug_cfg
307
+ params = dict(
308
+ scale_range=(0.85, 1.03), # done as part of shift_scale_rotate
309
+ bitmap_prob=0.05,
310
+ erosion_dilation_prob=0.02,
311
+ erosion_dilation_scale=(2, 3),
312
+ shear_prob=0.03,
313
+ shear_range_x=(0, 3.),
314
+ shear_range_y=(-3, 0),
315
+ shift_scale_rotate_prob=0.03,
316
+ shift_range_x=(0, 0.04),
317
+ shift_range_y=(0, 0.03),
318
+ rotate_range=2.,
319
+ grid_distort_prob=0.04,
320
+ grid_distort_range=0.05,
321
+ elastic_prob=0.04,
322
+ elastic_alpha=50.,
323
+ elastic_sigma=12.,
324
+ brightness_contrast_prob=0.03,
325
+ brightness_range=0.1,
326
+ constrast_range=0.1,
327
+ gaussian_noise_prob=0.08,
328
+ gaussian_noise_std=(0., 0.0175), # ~ variance 20 in [0, 255] pixel units
329
+ gaussian_blur_prob=0.03,
330
+ gaussian_blur_kernel_range=(3, 3),
331
+ image_compression_prob=0.1,
332
+ )
333
+
334
+ tv_pp += [
335
+ # this should be equivalent to initial resize & pad in Donut prepare_input()
336
+ ResizeKeepRatio(image_size, longest=1, interpolation=input_cfg.interpolation),
337
+ RandomCropOrPad(image_size, fill=input_cfg.fill_color),
338
+ ]
339
+
340
+ alb_pp += [
341
+ BitmapAlb(p=params['bitmap_prob']),
342
+ alb.OneOf([
343
+ ErosionAlb(params['erosion_dilation_scale']),
344
+ DilationAlb(params['erosion_dilation_scale'])
345
+ ],
346
+ p=params['erosion_dilation_prob']
347
+ ),
348
+ alb.Affine(
349
+ shear={
350
+ "x": params['shear_range_x'],
351
+ "y": params['shear_range_y']
352
+ },
353
+ border_mode=border_mode,
354
+ fill=fill,
355
+ p=params['shear_prob']
356
+ ),
357
+ alb.Affine(
358
+ translate_percent={
359
+ "x": params['shift_range_x'],
360
+ "y": params['shift_range_y'],
361
+ },
362
+ scale=params['scale_range'],
363
+ rotate=(-params['rotate_range'], params['rotate_range']),
364
+ interpolation=interpolation_mode,
365
+ border_mode=border_mode,
366
+ fill=fill,
367
+ p=params['shift_scale_rotate_prob'],
368
+ ),
369
+ alb.GridDistortion(
370
+ distort_limit=params['grid_distort_range'],
371
+ interpolation=interpolation_mode,
372
+ border_mode=border_mode,
373
+ fill=fill,
374
+ p=params['grid_distort_prob'],
375
+ ),
376
+ alb.Compose(
377
+ [
378
+ alb.Affine(
379
+ translate_px=(0, 5),
380
+ border_mode=border_mode,
381
+ fill=fill,
382
+ p=1.0,
383
+ ),
384
+ alb.ElasticTransform(
385
+ alpha=params['elastic_alpha'],
386
+ sigma=params['elastic_sigma'],
387
+ border_mode=border_mode,
388
+ fill=fill,
389
+ p=1.0,
390
+ ),
391
+ ],
392
+ p=params['elastic_prob'],
393
+ ),
394
+ alb.RandomBrightnessContrast(
395
+ brightness_limit=params['brightness_range'],
396
+ contrast_limit=params['constrast_range'],
397
+ p=params['brightness_contrast_prob'],
398
+ ),
399
+ alb.ImageCompression(
400
+ quality_range=(95, 100),
401
+ p=params['image_compression_prob'],
402
+ ),
403
+ alb.GaussNoise(
404
+ std_range=params['gaussian_noise_std'],
405
+ p=params['gaussian_noise_prob']
406
+ ),
407
+ alb.GaussianBlur(
408
+ blur_limit=params['gaussian_blur_kernel_range'],
409
+ p=params['gaussian_blur_prob'],
410
+ ),
411
+ ]
412
+ else:
413
+ # inference / eval
414
+ tv_pp += [
415
+ ResizeKeepRatio(image_size, longest=1, interpolation=input_cfg.interpolation),
416
+ CenterCropOrPad(image_size, fill=input_cfg.fill_color),
417
+ ]
418
+
419
+ if alb_pp:
420
+ # FIXME leave alb uncomposed too if composed=False?
421
+ tv_pp += [AlbWrapper(alb.Compose(alb_pp))]
422
+
423
+ tv_pp += [transforms.ToTensor()]
424
+ if do_normalize:
425
+ tv_pp += [transforms.Normalize(input_cfg.mean, input_cfg.std)]
426
+
427
+ return transforms.Compose(tv_pp) if composed else tv_pp
@@ -0,0 +1,203 @@
1
+ from dataclasses import asdict
2
+ from typing import Any, Dict, Optional, Union
3
+
4
+ from torchvision import transforms
5
+ from timm.data import (
6
+ ResizeKeepRatio,
7
+ CenterCropOrPad,
8
+ RandomResizedCropAndInterpolation,
9
+ create_transform,
10
+ )
11
+
12
+ from chugr.common import ImageInputCfg, ImageAugCfg
13
+ from .transforms_torch import ConvertColor
14
+
15
+
16
+ def build_transforms_image_timm(
17
+ input_cfg: ImageInputCfg,
18
+ is_training: bool = False,
19
+ do_normalize: bool = True,
20
+ do_convert: bool = False,
21
+ aug_cfg: Optional[Union[Dict[str, Any], ImageAugCfg]] = None,
22
+ composed: bool = True,
23
+ ):
24
+ """ Build image transforms leveraging timm's create_transform() functionality.
25
+
26
+ Args:
27
+ input_cfg:
28
+ is_training: In training mode, apply train transforms w/ augmentations
29
+ do_normalize: Enable normalization of ouput tensors by specified mean & std deviation.
30
+ aug_cfg:
31
+
32
+ Returns:
33
+
34
+ """
35
+ interpolation = input_cfg.interpolation or 'bicubic'
36
+ assert interpolation in ['bicubic', 'bilinear', 'random']
37
+
38
+ resize_mode = input_cfg.resize_mode or 'shortest'
39
+ assert resize_mode in ('shortest', 'longest', 'squash')
40
+
41
+ if isinstance(aug_cfg, dict):
42
+ aug_cfg = ImageAugCfg(**aug_cfg)
43
+ else:
44
+ aug_cfg = aug_cfg or ImageAugCfg.imagenet()
45
+
46
+ if is_training:
47
+ aug_cfg_dict = {k: v for k, v in asdict(aug_cfg).items() if v is not None}
48
+ aug_cfg_dict.setdefault('color_jitter', None) # disable by default
49
+ # FIXME map aug_cfg -> timm.create_transform args more carefully
50
+
51
+ train_transform = create_transform(
52
+ input_size=input_cfg.size,
53
+ is_training=True,
54
+ use_prefetcher=not do_normalize, # FIXME prefetcher mode disables normalize, but outputs np.array
55
+ hflip=0.,
56
+ mean=input_cfg.mean,
57
+ std=input_cfg.std,
58
+ re_mode='pixel',
59
+ interpolation=interpolation,
60
+ **aug_cfg_dict,
61
+ )
62
+ return train_transform
63
+ else:
64
+ if resize_mode == 'longest':
65
+ timm_crop_mode = 'border'
66
+ elif resize_mode == 'squash':
67
+ timm_crop_mode = 'squash'
68
+ else:
69
+ assert resize_mode == 'shortest'
70
+ timm_crop_mode = 'center'
71
+
72
+ eval_transform = create_transform(
73
+ input_size=input_cfg.size,
74
+ is_training=False,
75
+ use_prefetcher=not do_normalize, # FIXME prefetcher mode disables normalize, but outputs np.array
76
+ mean=input_cfg.mean,
77
+ std=input_cfg.std,
78
+ crop_pct=1.0,
79
+ crop_mode=timm_crop_mode,
80
+ # FIXME
81
+ # composed=composed,
82
+ )
83
+ return eval_transform
84
+
85
+
86
+ def build_transforms_image_basic(
87
+ input_cfg: ImageInputCfg,
88
+ is_training: bool = False,
89
+ do_normalize: bool = True,
90
+ do_convert: bool = False,
91
+ aug_cfg: Optional[Union[Dict[str, Any], ImageAugCfg]] = None,
92
+ composed: bool = True,
93
+ ):
94
+ """ Build image transfoms leveraging torchvision transforms.
95
+ """
96
+ if do_normalize:
97
+ normalize = transforms.Normalize(mean=input_cfg.mean, std=input_cfg.std)
98
+ else:
99
+ normalize = None
100
+
101
+ interpolation = input_cfg.interpolation or 'bicubic'
102
+ assert interpolation in ['bicubic', 'bilinear', 'random']
103
+ # NOTE random is ignored for interpolation_mode, so defaults to BICUBIC for inference if set
104
+ if interpolation == 'bilinear':
105
+ interpolation_mode = transforms.InterpolationMode.BILINEAR
106
+ else:
107
+ interpolation_mode = transforms.InterpolationMode.BICUBIC
108
+
109
+ resize_mode = input_cfg.resize_mode or 'shortest'
110
+ assert resize_mode in ('shortest', 'longest', 'squash')
111
+
112
+ if isinstance(aug_cfg, dict):
113
+ aug_cfg = ImageAugCfg(**aug_cfg)
114
+ else:
115
+ aug_cfg = aug_cfg or ImageAugCfg.imagenet()
116
+
117
+ if is_training:
118
+ image_size = input_cfg.size
119
+ if resize_mode == 'shortest':
120
+ if isinstance(image_size, (tuple, list)) and image_size[0] == image_size[1]:
121
+ image_size = image_size[0] # w/ scalar for final resize in RRC will use shortest edge
122
+ # FIXME note we don't have good option for 'longest' resizing w/ RRC
123
+
124
+ transform_list = [
125
+ # like torchvision.transforms.RandomResizedCrop but supports randomized interpolation for robustness
126
+ RandomResizedCropAndInterpolation(
127
+ image_size,
128
+ scale=aug_cfg.scale or (1.0, 1.0),
129
+ ratio=aug_cfg.ratio or (1.0, 1.0),
130
+ interpolation=interpolation,
131
+ ),
132
+ ]
133
+
134
+ if do_convert:
135
+ transform_list.append(ConvertColor(mode=input_cfg.mode))
136
+
137
+ if aug_cfg.color_jitter_prob:
138
+ assert aug_cfg.color_jitter is not None and len(aug_cfg.color_jitter) == 4
139
+ if aug_cfg.color_jitter_prob is not None:
140
+ transform_list.append(
141
+ transforms.RandomApply(
142
+ transforms.ColorJitter(
143
+ *aug_cfg.color_jitter,
144
+ ),
145
+ p=aug_cfg.color_jitter_prob,
146
+ )
147
+ )
148
+ elif aug_cfg.color_jitter is not None:
149
+ transform_list.append(transforms.ColorJitter(*aug_cfg.color_jitter))
150
+
151
+ if aug_cfg.grayscale_prob:
152
+ transform_list.append(transforms.RamndomGrayscale(aug_cfg.grayscale_prob))
153
+
154
+ if aug_cfg.gaussian_blur_prob:
155
+ gaussian_blur_kernel = aug_cfg.gaussian_blur_kernel_size or 23
156
+ transforms.RandomApply([
157
+ transforms.GaussianBlur(
158
+ kernel_size=gaussian_blur_kernel,
159
+ )],
160
+ p=aug_cfg.gaussian_blur_prob,
161
+ )
162
+
163
+ else:
164
+ image_size = input_cfg.size
165
+
166
+ if resize_mode == 'longest':
167
+ transform_list = [
168
+ ResizeKeepRatio(image_size, interpolation=interpolation_mode, longest=1),
169
+ CenterCropOrPad(image_size, fill=input_cfg.fill_color)
170
+ ]
171
+ elif resize_mode == 'squash':
172
+ if isinstance(image_size, int):
173
+ image_size = (image_size, image_size)
174
+
175
+ transform_list = [
176
+ transforms.Resize(image_size, interpolation=interpolation_mode, antialias=True),
177
+ ]
178
+ else:
179
+ assert resize_mode == 'shortest'
180
+ if not isinstance(image_size, (tuple, list)):
181
+ image_size = (image_size, image_size)
182
+
183
+ if image_size[0] == image_size[1]:
184
+ # simple case, use torchvision built-in Resize w/ shortest edge mode (scalar size arg)
185
+ transform_list = [
186
+ transforms.Resize(image_size[0], interpolation=interpolation_mode, antialias=True)
187
+ ]
188
+ else:
189
+ # resize shortest edge to matching target dim for non-square target
190
+ transform_list = [ResizeKeepRatio(image_size)]
191
+
192
+ transform_list += [transforms.CenterCrop(image_size)]
193
+
194
+ if do_convert:
195
+ transform_list.append(ConvertColor(mode=input_cfg.mode))
196
+ # end if is_training
197
+
198
+ transform_list += [transforms.ToTensor()]
199
+
200
+ if normalize is not None:
201
+ transform_list += [normalize]
202
+
203
+ return transforms.Compose(transform_list) if composed else transform_list