Safemotion Lib
Loading...
Searching...
No Matches
transforms.py
Go to the documentation of this file.
1# encoding: utf-8
2"""
3@author: liaoxingyu
4@contact: sherlockliao01@gmail.com
5"""
6
7__all__ = ['ToTensor', 'RandomErasing', 'RandomPatch', 'AugMix',]
8
9import math
10import random
11from collections import deque
12
13import numpy as np
14from PIL import Image
15
16from .functional import to_tensor, augmentations_reid
17
18
19class ToTensor(object):
20 """Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor.
21
22 Converts a PIL Image or numpy.ndarray (H x W x C) in the range
23 [0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 255.0]
24 if the PIL Image belongs to one of the modes (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)
25 or if the numpy.ndarray has dtype = np.uint8
26
27 In the other cases, tensors are returned without scaling.
28 """
29
30 def __call__(self, pic):
31 """
32 Args:
33 pic (PIL Image or numpy.ndarray): Image to be converted to tensor.
34
35 Returns:
36 Tensor: Converted image.
37 """
38 return to_tensor(pic)
39
40 def __repr__(self):
41 return self.__class__.__name__ + '()'
42
43
44class RandomErasing(object):
45 """ Randomly selects a rectangle region in an image and erases its pixels.
46 'Random Erasing Data Augmentation' by Zhong et al.
47 See https://arxiv.org/pdf/1708.04896.pdf
48 Args:
49 probability: The probability that the Random Erasing operation will be performed.
50 sl: Minimum proportion of erased area against input image.
51 sh: Maximum proportion of erased area against input image.
52 r1: Minimum aspect ratio of erased area.
53 mean: Erasing value.
54 """
55
56 def __init__(self, probability=0.5, sl=0.02, sh=0.4, r1=0.3, mean=255 * (0.49735, 0.4822, 0.4465)):
57 self.probability = probability
58 self.mean = mean
59 self.sl = sl
60 self.sh = sh
61 self.r1 = r1
62
63 def __call__(self, img):
64 img = np.asarray(img, dtype=np.float32).copy()
65 if random.uniform(0, 1) > self.probability:
66 return img
67
68 for attempt in range(100):
69 area = img.shape[0] * img.shape[1]
70 target_area = random.uniform(self.sl, self.sh) * area
71 aspect_ratio = random.uniform(self.r1, 1 / self.r1)
72
73 h = int(round(math.sqrt(target_area * aspect_ratio)))
74 w = int(round(math.sqrt(target_area / aspect_ratio)))
75
76 if w < img.shape[1] and h < img.shape[0]:
77 x1 = random.randint(0, img.shape[0] - h)
78 y1 = random.randint(0, img.shape[1] - w)
79 if img.shape[2] == 3:
80 img[x1:x1 + h, y1:y1 + w, 0] = self.mean[0]
81 img[x1:x1 + h, y1:y1 + w, 1] = self.mean[1]
82 img[x1:x1 + h, y1:y1 + w, 2] = self.mean[2]
83 else:
84 img[x1:x1 + h, y1:y1 + w, 0] = self.mean[0]
85 return img
86 return img
87
88
89class RandomPatch(object):
90 """Random patch data augmentation.
91 There is a patch pool that stores randomly extracted pathces from person images.
92 For each input image, RandomPatch
93 1) extracts a random patch and stores the patch in the patch pool;
94 2) randomly selects a patch from the patch pool and pastes it on the
95 input (at random position) to simulate occlusion.
96 Reference:
97 - Zhou et al. Omni-Scale Feature Learning for Person Re-Identification. ICCV, 2019.
98 - Zhou et al. Learning Generalisable Omni-Scale Representations
99 for Person Re-Identification. arXiv preprint, 2019.
100 """
101
102 def __init__(self, prob_happen=0.5, pool_capacity=50000, min_sample_size=100,
103 patch_min_area=0.01, patch_max_area=0.5, patch_min_ratio=0.1,
104 prob_rotate=0.5, prob_flip_leftright=0.5,
105 ):
106 self.prob_happen = prob_happen
107
108 self.patch_min_area = patch_min_area
109 self.patch_max_area = patch_max_area
110 self.patch_min_ratio = patch_min_ratio
111
112 self.prob_rotate = prob_rotate
113 self.prob_flip_leftright = prob_flip_leftright
114
115 self.patchpool = deque(maxlen=pool_capacity)
116 self.min_sample_size = min_sample_size
117
118 def generate_wh(self, W, H):
119 area = W * H
120 for attempt in range(100):
121 target_area = random.uniform(self.patch_min_area, self.patch_max_area) * area
122 aspect_ratio = random.uniform(self.patch_min_ratio, 1. / self.patch_min_ratio)
123 h = int(round(math.sqrt(target_area * aspect_ratio)))
124 w = int(round(math.sqrt(target_area / aspect_ratio)))
125 if w < W and h < H:
126 return w, h
127 return None, None
128
129 def transform_patch(self, patch):
130 if random.uniform(0, 1) > self.prob_flip_leftright:
131 patch = patch.transpose(Image.FLIP_LEFT_RIGHT)
132 if random.uniform(0, 1) > self.prob_rotate:
133 patch = patch.rotate(random.randint(-10, 10))
134 return patch
135
136 def __call__(self, img):
137 if isinstance(img, np.ndarray):
138 img = Image.fromarray(img.astype(np.uint8))
139
140 W, H = img.size # original image size
141
142 # collect new patch
143 w, h = self.generate_wh(W, H)
144 if w is not None and h is not None:
145 x1 = random.randint(0, W - w)
146 y1 = random.randint(0, H - h)
147 new_patch = img.crop((x1, y1, x1 + w, y1 + h))
148 self.patchpool.append(new_patch)
149
150 if len(self.patchpool) < self.min_sample_size:
151 return img
152
153 if random.uniform(0, 1) > self.prob_happen:
154 return img
155
156 # paste a randomly selected patch on a random position
157 patch = random.sample(self.patchpool, 1)[0]
158 patchW, patchH = patch.size
159 x1 = random.randint(0, W - patchW)
160 y1 = random.randint(0, H - patchH)
161 patch = self.transform_patch(patch)
162 img.paste(patch, (x1, y1))
163
164 return img
165
166
167class AugMix(object):
168 """ Perform AugMix augmentation and compute mixture.
169 Args:
170 aug_prob_coeff: Probability distribution coefficients.
171 mixture_width: Number of augmentation chains to mix per augmented example.
172 mixture_depth: Depth of augmentation chains. -1 denotes stochastic depth in [1, 3]'
173 severity: Severity of underlying augmentation operators (between 1 to 10).
174 """
175
176 def __init__(self, aug_prob_coeff=1, mixture_width=3, mixture_depth=-1, severity=1):
177 self.aug_prob_coeff = aug_prob_coeff
178 self.mixture_width = mixture_width
179 self.mixture_depth = mixture_depth
180 self.severity = severity
181 self.aug_list = augmentations_reid
182
183 def __call__(self, image):
184 """Perform AugMix augmentations and compute mixture.
185 Returns:
186 mixed: Augmented and mixed image.
187 """
188 ws = np.float32(
189 np.random.dirichlet([self.aug_prob_coeff] * self.mixture_width))
190 m = np.float32(np.random.beta(self.aug_prob_coeff, self.aug_prob_coeff))
191
192 image = np.asarray(image, dtype=np.float32).copy()
193 mix = np.zeros_like(image)
194 h, w = image.shape[0], image.shape[1]
195 for i in range(self.mixture_width):
196 image_aug = Image.fromarray(image.copy().astype(np.uint8))
197 depth = self.mixture_depth if self.mixture_depth > 0 else np.random.randint(1, 4)
198 for _ in range(depth):
199 op = np.random.choice(self.aug_list)
200 image_aug = op(image_aug, self.severity, (w, h))
201 mix += ws[i] * np.asarray(image_aug, dtype=np.float32)
202
203 mixed = (1 - m) * image + m * mix
204 return mixed
__init__(self, aug_prob_coeff=1, mixture_width=3, mixture_depth=-1, severity=1)
__init__(self, probability=0.5, sl=0.02, sh=0.4, r1=0.3, mean=255 *(0.49735, 0.4822, 0.4465))
Definition transforms.py:56
__init__(self, prob_happen=0.5, pool_capacity=50000, min_sample_size=100, patch_min_area=0.01, patch_max_area=0.5, patch_min_ratio=0.1, prob_rotate=0.5, prob_flip_leftright=0.5)