# Kornia 0.2.1 release

Data augmentation framework, deep descriptors and more.

In this release we support compatibility between `kornia.augmentation`

and `torchvision.transforms`

.

We now support all the same existing operations with torch.Tensor in the GPU with extra features such as returning for each operator the transformation matrix generated to produce such transformation.

```
import kornia as K
import torchvision as T
# kornia
transform_fcn = torch.nn.Sequential(
K.augmentation.RandomAffine(
[-45., 45.], [0., 0.5], [0.5, 1.5], [0., 0.5], return_transform=True),
K.color.Normalize(0.1307, 0.3081),
)
# torchvision
transform_fcn = T.transforms.Compose([
T.transforms.RandomAffine(
[-45., 45.], [0., 0.5], [0.5, 1.5], [0., 0.5]),
T.transforms.ToTensor(),
T.transforms.Normalize((0.1307,), (0.3081,)),
])
```

Check the online documentations with the updated API [DOCS]

Check this Google Colab to see how to reproduce same results [Colab]

##
`kornia.augmentation`

as a framework

In addition, we have re-designed `kornia.augmentation`

such in a way that users can easily contribute with more operators, or just use it as a framework to create their custom operators.

Each of the `kornia.augmentation`

modules inherit from `AugmentationBase`

and one can easily define a new operator by creating a subclass and overriding a couple of methods.

Let’s take a look at a custom `MyRandomRotation`

. The class inherits from `AugmentationBase`

making it a `nn.Module`

so that can be stacked in a `nn.Sequential`

to compute chained transformations.

To implement a new functionality two things needed: override **get_params** and **apply**.

The `get_params`

receives the shape of the input tensor and returns a dictionary with the parameters to use in the `apply`

function.

The `apply`

function receives as input a tensor and the dictionary defined in `get_params`

and returns a tuple with the transformed input and the transformation applied to it.

```
class MyRandomRotation(AugmentationBase):
def __init__(self, angle: float, return_transform: bool = True) -> None:
super(MyRandomRotation, self).__init__(self.apply, return_transform)
self.angle = angle
def get_params(self, batch_shape: torch.Size) -> Dict[str, torch.Tensor]:
angles_rad torch.Tensor = torch.rand(batch_shape) * K.pi
angles_deg = kornia.rad2deg(angles_rad) * self.angle
return dict(angles=angles_deg)
def apply(self, input: torch.Tensor, params: Dict[str, torch.Tensor]):
# compute transformation
angles: torch.Tensor = params['angles'].type_as(input)
center = torch.tensor([[W / 2, H / 2]]).type_as(input)
transform = K.get_rotation_matrix2d(
center, angles, torch.ones_like(angles))
# apply transformation
output = K.warp_affine(input, transform, (H, W))
return (output, transform)
# how to use it
# load an image and cast to tensor
img1: torch.Tensor = imread(...) # BxDxHxW
# instantiate and apply the transform
aug = MyRandomRotation(45., return_transformation=True)
img2, transform = aug(img1) # BxDxHxW - Bx3x3
```

Please, do not hesitate to check the release notes on GitHub to learn about the new library features and get more details.

Have a happy coding day

**The Kornia team**