from fastai.vision.all import *
from fastai.vision.augment import _grid
= untar_data(URLs.IMAGENETTE_320) path
= Image.open(path.ls()[0].ls()[3].ls()[0])
img = TensorImage(image2tensor(img)[None]/255.) img
=.99)[1] aug_transforms(max_lighting
Brightness -- {'max_lighting': 0.99, 'p': 1.0, 'draw': None, 'batch': False}:
encodes: (TensorImage,object) -> encodes
decodes:
=.99)[1](img.repeat(12,1,1,1))) show_images(aug_transforms(max_lighting
RandTransform
Notice that p, can be set to control the probability of a transform being applied.
class RandTransform(DisplayedTransform):
"A transform that before_call its state at each `__call__`"
= True,None,[],0
do,nm,supports,split_idx def __init__(self,
float=1., # Probability of applying Transform
p:str=None,
nm:callable=None, # Optional batchwise preprocessing function
before_call:**kwargs
):'p')
store_attr(super().__init__(**kwargs)
self.before_call = ifnone(before_call,self.before_call)
def before_call(self,
b, int, # Index of the train/valid dataset
split_idx:
):"This function can be overridden. Set `self.do` based on `self.p`"
self.do = self.p==1. or random.random() < self.p
def __call__(self,
b, int=None, # Index of the train/valid dataset
split_idx:**kwargs
):self.before_call(b, split_idx=split_idx)
return super().__call__(b, split_idx=split_idx, **kwargs) if self.do else b
Affine
Fastai has many affine transforms. These include crop, zoom, flip etc. Lets go through some now.
xy_grid.shape
torch.Size([1, 320, 480, 2])
def show_grid(xy_grid):
=torch.zeros_like(xy_grid)[...,0,None]
neutral_dim=torch.cat((xy_grid,neutral_dim),dim=3)
normal_grid= (normal_grid>1).int() + (normal_grid<-1).int()
bad_mask =-bad_mask.sum(-1)*10
bad_mask+=bad_mask[...,None]
normal_grid+1)/2).clip(0,1)) show_images(((normal_grid
=torch.linspace(-1,1,img.shape[-2])
y_coords=torch.linspace(-1,1,img.shape[-1])
x_coords=torch.meshgrid(x_coords, y_coords, indexing='xy')
xy_grid=torch.stack(xy_grid,dim=2)[None]
xy_grid show_grid(xy_grid)
xy_grid
tensor([[[[-1.0000, -1.0000],
[-0.9958, -1.0000],
[-0.9916, -1.0000],
...,
[ 0.9916, -1.0000],
[ 0.9958, -1.0000],
[ 1.0000, -1.0000]],
[[-1.0000, -0.9937],
[-0.9958, -0.9937],
[-0.9916, -0.9937],
...,
[ 0.9916, -0.9937],
[ 0.9958, -0.9937],
[ 1.0000, -0.9937]],
[[-1.0000, -0.9875],
[-0.9958, -0.9875],
[-0.9916, -0.9875],
...,
[ 0.9916, -0.9875],
[ 0.9958, -0.9875],
[ 1.0000, -0.9875]],
...,
[[-1.0000, 0.9875],
[-0.9958, 0.9875],
[-0.9916, 0.9875],
...,
[ 0.9916, 0.9875],
[ 0.9958, 0.9875],
[ 1.0000, 0.9875]],
[[-1.0000, 0.9937],
[-0.9958, 0.9937],
[-0.9916, 0.9937],
...,
[ 0.9916, 0.9937],
[ 0.9958, 0.9937],
[ 1.0000, 0.9937]],
[[-1.0000, 1.0000],
[-0.9958, 1.0000],
[-0.9916, 1.0000],
...,
[ 0.9916, 1.0000],
[ 0.9958, 1.0000],
[ 1.0000, 1.0000]]]])
show_images(F.grid_sample(img,xy_grid))
def make_grid(x_coords,y_coords):
=torch.meshgrid(x_coords, y_coords, indexing='xy')
xy_grid=torch.stack(xy_grid,dim=2)[None]
xy_gridreturn xy_grid
=torch.linspace(-1,1,img.shape[-2])
y_coords=torch.linspace(-1,1,img.shape[-1])
x_coords
make_grid(y_coords) show_grid(xy_grid)
Slide Left
+1,y_coords),) show_grid(make_grid(x_coords
-1*x_coords,y_coords))) show_images(F.grid_sample(img,make_grid(
Flip
-1*x_coords,y_coords),) show_grid(make_grid(
2.*x_coords,y_coords))) show_images(F.grid_sample(img,make_grid(
Squish/Resize x dim
2*x_coords,y_coords),) show_grid(make_grid(
2*x_coords,y_coords))) show_images(F.grid_sample(img,make_grid(
y_coords.shape
torch.Size([320])
x_coords.shape
torch.Size([480])
Why can’t we use the technique we have used to implement rotate/warp?
I mostly introduced the previous techniques to make things easy to understand by making x and y indepentdent, but affine transformations can work off of the current x and y values, which takes a bit more code to implement. Lets jump straight into building these like fastai.
F.affine_grid
Affine grids work on much smaller grids.
=torch.tensor([[1.,0,.5],
translate_grid0,1,0]]) [
=F.affine_grid(translate_grid[None], img.shape)
coords_grid show_grid(coords_grid)
show_images(F.grid_sample(img,coords_grid))
https://en.wikipedia.org/wiki/Affine_transformation
1,0,0],
torch.tensor([[0,1,0]]) [
tensor([[1, 0, 0],
[0, 1, 0]])
1.,0,0],
torch.tensor([[0,1,0]]) [
tensor([[1., 0., 0.],
[0., 1., 0.]])
=torch.tensor([[1.,0,0],
identity_grid0,1,0]])
[=F.affine_grid(identity_grid[None], img.shape)
coords_grid
show_grid(coords_grid) show_images(F.grid_sample(img,coords_grid))
=torch.tensor([[math.cos(.5),-math.sin(.5),0],
rot_grid.5),math.cos(.5),0]])
[math.sin(=F.affine_grid(rot_grid[None], img.shape)
coords_grid
show_grid(coords_grid) show_images(F.grid_sample(img,coords_grid))
Rotate
.5) math.cos(
0.8775825618903728
=torch.tensor([[math.cos(1),math.sin(1),0],
rotate_grid-math.sin(1),math.cos(1),0]])
[=F.affine_grid(rotate_grid[None], img.shape)
coords_grid show_grid(coords_grid)
show_images(F.grid_sample(img,coords_grid))
=torch.tensor([[1,.5,0],
shear_grid0,1,0]])
[=F.affine_grid(shear_grid[None], img.shape)
coords_grid show_grid(coords_grid)
show_images(F.grid_sample(img,coords_grid))
How would we warp/skew?
=torch.tensor([[1.,.5,0],
warp_grid0,1,0]])
[=F.affine_grid(warp_grid[None], img.shape)
coords_grid show_grid(coords_grid)
show_images(F.grid_sample(img,coords_grid))
Combining affine augmentations
Lets look at the affine grid identity.
=torch.tensor([[1.,0,0],
affine_grid0,1,0]])
[=F.affine_grid(affine_grid[None], img.shape)
coords_grid show_grid(coords_grid)
Does this affine grid identity look familiar? Can you think of a way to combine affine transforms?
Implementation
def combine_affines(affines):
=lambda a:torch.cat((a,torch.tensor([.0,0,1])[None]))
id_row=id_row(affines[0])
comb_matfor a in affines:
@=id_row(a)
comb_matreturn comb_mat[:2]
=combine_affines([warp_grid,rotate_grid,translate_grid]) wrt_grid
wrt_grid
tensor([[-0.3012, 1.3818, -0.3012],
[-0.8415, 0.5403, -0.8415]])
=F.affine_grid(wrt_grid[None], img.shape) coords_grid
show_grid(coords_grid)
show_images(F.grid_sample(img,coords_grid)),show_images(img)
(None, None)
_BrightnessLogit??
Object `_BrightnessLogit` not found.
Lighting
+.4)) show_images((img
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
=TensorImage(torch.tensor([.01* i for i in range(0,101)]))
x= lambda x:(2*(x-0.5)+0.5).clamp(0,1) #blue line
f_lin= lambda x:2*x #red line
f_log'b',x,torch.sigmoid(f_log(logit(x))),'r'); plt.plot(x,f_lin(x),
What is special about logit in relationship to sigmoid?
+.5) show_images(img
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
+logit(torch.tensor(.85)))) show_images(torch.sigmoid(logit(img)
show_images(img)
logit??
How to do contrast?
*4)) show_images(torch.sigmoid(logit(img)
Next Section
Open other notebook