| |
| """ |
| TensorImageNoised class, and corresponding transforms. |
| |
| This class exists so autoencoder inputs can be subjected to augmentation transforms without the outputs being affected when using fastai dataloaders. |
| """ |
|
|
| from fastai import * |
| from fastai.vision.all import * |
|
|
| class PILImageNoised(PILImage): pass |
| class TensorImageNoised(TensorImage): pass |
| PILImageNoised._tensor_cls = TensorImageNoised |
|
|
| class AddNoiseTransform(Transform): |
| "Add noise to image" |
| split_idx=0 |
| order = 11 |
| def __init__(self, noise_factor=0.3): self.noise_factor = noise_factor |
| def encodes(self, o:TensorImageNoised): return o + (self.noise_factor * torch.randn(*o.shape).to(o.device)) |
|
|
| class RandomErasingTransform(RandTransform): |
| "Randomly selects a rectangle region in an image and randomizes its pixels." |
| order = 100 |
| def __init__(self, |
| p:float=0.5, |
| sl:float=0., |
| sh:float=0.3, |
| min_aspect:float=0.3, |
| max_count:int=1 |
| ): |
| store_attr() |
| super().__init__(p=p) |
| self.log_ratio = (math.log(min_aspect), math.log(1/min_aspect)) |
|
|
| def _bounds(self, area, img_h, img_w): |
| r_area = random.uniform(self.sl,self.sh) * area |
| aspect = math.exp(random.uniform(*self.log_ratio)) |
| return self._slice(r_area*aspect, img_h) + self._slice(r_area/aspect, img_w) |
| |
| def _slice(self, area, sz): |
| bound = int(round(math.sqrt(area))) |
| loc = random.randint(0, max(sz-bound, 0)) |
| return loc,loc+bound |
| def cutout_gaussian(self, |
| x:TensorImageNoised, |
| areas:list |
| ): |
| "Replace all `areas` in `x` with N(0,1) noise" |
| chan,img_h,img_w = x.shape[-3:] |
| for rl,rh,cl,ch in areas: x[..., rl:rh, cl:ch].normal_() |
| return x |
|
|
| def encodes(self,x:TensorImageNoised): |
| count = random.randint(1, self.max_count) |
| _,img_h,img_w = x.shape[-3:] |
| area = img_h*img_w/count |
| areas = [self._bounds(area, img_h, img_w) for _ in range(count)] |
| return self.cutout_gaussian(x, areas) |